<commit_before>// electronまわり読み込み
const electron = require('electron');
const {app} = electron;
const {Menu} = electron;
const {ipcMain} = electron;
const {shell} = electron;
const {BrowserWindow} = electron;

const electronOpenLinkInBrowser = require("electron-open-link-in-browser");
const itunes = require('playback');
const mastodon = require('mastodon-api');
const fs = require('fs');

const Protocol = "https";
const configFilePath = __dirname +"/config/auth.json";

let authJson = null;
let baseUrl = Protocol+"://";
let beforeMusic = null;
let mainWindow = null;

app.on('ready', () => {
    // Create the Application's main menu
    const template = [
        {
            label: "Application",
            submenu: [
                { label: "About Application", selector: "orderFrontStandardAboutPanel:" },
                { type: "separator" },
                { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
            ]
        },
        {
            label: "Edit",
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" },
            ]
        }
    ];

    const menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);

    try {
        authJson = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
    } catch (e) {
        //console.log(e);
        console.log("nothing auth.json");
        authJson = JSON.parse(fs.readFileSync(configFilePath+".sample", 'utf8'));
    }

    // mainWindowを作成（windowの大きさや、Kioskモードにするかどうかなどもここで定義できる）
    mainWindow = new BrowserWindow({width: 600, height: 600});
    // ChromiumのDevツールを開く
    mainWindow.webContents.openDevTools();

    // アプリ作成設定を受け取るフック
    ipcMain.on('host', function(event, data){
        console.log(data);
        baseUrl += data.host;
        mastodon.createOAuthApp(baseUrl + '/api/v1/apps', authJson.app_name, authJson.scope)
            .catch(err => console.error(err))
            .then((res) => {
                console.log("host");
                console.log(res);
                if(res.client_id === undefined || res.client_secret === undefined ){
                    return null;
                }else{
                    authJson.host = data.host;
                    authJson.client_id = res.client_id;
                    authJson.client_secret = res.client_secret;
                    fs.writeFile(configFilePath, JSON.stringify(authJson, null, '    '));

                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                    return mastodon.getAuthorizationUrl(res.client_id, res.client_secret, baseUrl);
                }
            }).then(url => {
                if(url === null){
                    mainWindow.loadURL('file://' + __dirname + '/host.html');
                }else{
                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                    mastodon.getAuthorizationUrl(authJson.client_id, authJson.client_secret, baseUrl, authJson.scope)
                        .then(url => {
                            console.log(url);
                            shell.openExternal(url);
                        });
                }
            });
    });

    // authorization_codeをもらうためのフック
    ipcMain.on('token', function( event, data ){
        mainWindow.loadURL('file://' + __dirname + '/index.html');
        mastodon.getAccessToken(authJson.client_id, authJson.client_secret, data.authorization_code, baseUrl)
            .catch(err => console.error(err))
            .then(accessToken => {
                console.log(`This is the access token. Save it!\n${accessToken}`);
                if(accessToken === undefined){
                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                }else{
                    authJson.access_token = accessToken;
                    fs.writeFile(configFilePath, JSON.stringify(authJson, null, '    '));

                    postNowplaying(accessToken);
                }
            });
    });

    // now playing情報を初期化するフック
    ipcMain.on('init_now_playing', function( event ){
        itunes.currentTrack(function(data){
            let message = "#now_play_don ";
            if(data != null){
                message += data.name+" / "+data.album+" / "+data.artist;
            }else{
                message += "music / album / artist";
            }
            event.sender.send('now_playing', {message: message});
        });
    });

    if(authJson.client_id === null || authJson.client_secret === null || authJson.host === null){
        mainWindow.loadURL('file://' + __dirname + '/host.html');
    }else if(authJson.access_token === null){
        baseUrl += authJson.host;
        mainWindow.loadURL('file://' + __dirname + '/auth.html');
        mastodon.getAuthorizationUrl(authJson.client_id, authJson.client_secret, baseUrl, authJson.scope)
            .then(url => {
                console.log(url);
                shell.openExternal(url);
            });
    }else{
        baseUrl += authJson.host;
        // Electronに表示するhtmlを絶対パスで指定（相対パスだと動かない）
        mainWindow.loadURL('file://' + __dirname + '/index.html');

        postNowplaying(authJson.access_token);
    }

    mainWindow.on('closed', function() {
        mainWindow = null;
        app.quit();
    });
});

function postNowplaying(access_token){
    const M = new mastodon({
        access_token: access_token,
        timeout_ms: 60 * 1000,
        api_url: baseUrl+"/api/v1/"
    });

    itunes.on('playing', function(data){
        if(!(beforeMusic === data.name)){
            let message = "#now_play_don "+data.name+" / "+data.album+" / "+data.artist;

            M.post('statuses', {status: message}, function (err, data, res) {
                if (err){
                    console.log(err);
                }
            });

            mainWindow.webContents.send('now_playing', { message: message });
        }
        beforeMusic = data.name;
    });
};
<commit_msg>bugfix missing file<commit_after>// electronまわり読み込み
const electron = require('electron');
const {app} = electron;
const {Menu} = electron;
const {ipcMain} = electron;
const {shell} = electron;
const {BrowserWindow} = electron;

const electronOpenLinkInBrowser = require("electron-open-link-in-browser");
const itunes = require('playback');
const mastodon = require('mastodon-api');
const fs = require('fs');

const Protocol = "https";
const configFileName = "auth.json";

let authJson = null;
let baseUrl = Protocol+"://";
let beforeMusic = null;
let mainWindow = null;
let configFilePath = "";

app.on('ready', () => {
    // Create the Application's main menu
    const template = [
        {
            label: "Application",
            submenu: [
                { label: "About Application", selector: "orderFrontStandardAboutPanel:" },
                { type: "separator" },
                { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
            ]
        },
        {
            label: "Edit",
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" },
            ]
        }
    ];

    const menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);

    try {
        configFilePath = app.getPath('userData') + configFileName;
        console.log(configFilePath);
        authJson = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
    } catch (e) {
        //console.log(e);
        console.log("nothing auth.json");
        authJson = JSON.parse(fs.readFileSync(__dirname+"/config/"+configFileName+".sample", 'utf8'));
    }

    // mainWindowを作成（windowの大きさや、Kioskモードにするかどうかなどもここで定義できる）
    mainWindow = new BrowserWindow({width: 600, height: 600});

    // アプリ作成設定を受け取るフック
    ipcMain.on('host', function(event, data){
        console.log(data);
        baseUrl += data.host;
        mastodon.createOAuthApp(baseUrl + '/api/v1/apps', authJson.app_name, authJson.scope)
            .catch(err => console.error(err))
            .then((res) => {
                console.log("host");
                console.log(res);
                if(res.client_id === undefined || res.client_secret === undefined ){
                    return null;
                }else{
                    authJson.host = data.host;
                    authJson.client_id = res.client_id;
                    authJson.client_secret = res.client_secret;
                    fs.writeFile(configFilePath, JSON.stringify(authJson, null, '    '));

                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                    return mastodon.getAuthorizationUrl(res.client_id, res.client_secret, baseUrl);
                }
            }).then(url => {
                if(url === null){
                    mainWindow.loadURL('file://' + __dirname + '/host.html');
                }else{
                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                    mastodon.getAuthorizationUrl(authJson.client_id, authJson.client_secret, baseUrl, authJson.scope)
                        .then(url => {
                            console.log(url);
                            shell.openExternal(url);
                        });
                }
            });
    });

    // authorization_codeをもらうためのフック
    ipcMain.on('token', function( event, data ){
        mainWindow.loadURL('file://' + __dirname + '/index.html');
        mastodon.getAccessToken(authJson.client_id, authJson.client_secret, data.authorization_code, baseUrl)
            .catch(err => console.error(err))
            .then(accessToken => {
                console.log(`This is the access token. Save it!\n${accessToken}`);
                if(accessToken === undefined){
                    mainWindow.loadURL('file://' + __dirname + '/auth.html');
                }else{
                    authJson.access_token = accessToken;
                    fs.writeFile(configFilePath, JSON.stringify(authJson, null, '    '));

                    postNowplaying(accessToken);
                }
            });
    });

    // now playing情報を初期化するフック
    ipcMain.on('init_now_playing', function( event ){
        itunes.currentTrack(function(data){
            let message = "#now_play_don ";
            if(data != null){
                message += data.name+" / "+data.album+" / "+data.artist;
            }else{
                message += "music / album / artist";
            }
            event.sender.send('now_playing', {message: message});
        });
    });

    if(authJson.client_id === null || authJson.client_secret === null || authJson.host === null){
        mainWindow.loadURL('file://' + __dirname + '/host.html');
    }else if(authJson.access_token === null){
        baseUrl += authJson.host;
        mainWindow.loadURL('file://' + __dirname + '/auth.html');
        mastodon.getAuthorizationUrl(authJson.client_id, authJson.client_secret, baseUrl, authJson.scope)
            .then(url => {
                console.log(url);
                shell.openExternal(url);
            });
    }else{
        baseUrl += authJson.host;
        // Electronに表示するhtmlを絶対パスで指定（相対パスだと動かない）
        mainWindow.loadURL('file://' + __dirname + '/index.html');

        postNowplaying(authJson.access_token);
    }

    mainWindow.on('closed', function() {
        mainWindow = null;
        app.quit();
    });
});

function postNowplaying(access_token){
    const M = new mastodon({
        access_token: access_token,
        timeout_ms: 60 * 1000,
        api_url: baseUrl+"/api/v1/"
    });

    itunes.on('playing', function(data){
        if(!(beforeMusic === data.name)){
            let message = "#now_play_don "+data.name+" / "+data.album+" / "+data.artist;

            M.post('statuses', {status: message}, function (err, data, res) {
                if (err){
                    console.log(err);
                }
            });

            mainWindow.webContents.send('now_playing', { message: message });
        }
        beforeMusic = data.name;
    });
};
<|endoftext|>


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

const getIndexURL = (loadFromFile, filename, devPort) => {

    const createFileURL = (filename) => {
        return `file://${__dirname}/${filename}`;
    };

    const createServerURL = (filename, devPort) => {
        const protocol = 'http';
        const domain = 'localhost';
        const port = `:${devPort}/`;

        return `${protocol}://${domain}${port}${filename}`;
    };

    if (loadFromFile) {
        return createFileURL(filename);
    }
    else {
        return createServerURL(filename, devPort);
    }
};

const startLocalServer = (devPort) => {
    require('child_process').exec(`cd ../../ && scramjs/scram-engine/node_modules/.bin/http-server -p ${devPort}`, (err, stdout, stderr) => {
        if (!err) {
            console.log(stdout);
        }
        else {
            console.log(stderr);
        }
    });
};

const launchApp = (indexURL, devMode) => {
    const electron = require('electron');
    const app = electron.app;
    const BrowserWindow = electron.BrowserWindow;
    const ipcMain = electron.ipcMain;

    app.commandLine.appendSwitch('--disable-http-cache');

    let mainWindow = null;

    app.on('ready', () => {
            mainWindow = new BrowserWindow({
                show: devMode
            });

            mainWindow.loadURL(indexURL);

            if (devMode) {
                mainWindow.webContents.openDevTools();
            }
    });

    ipcMain.on('asynchronous-message', (e, ...args) => {
        console.log(...args);
    });
};

const init = () => {
    const argOptions = process.argv.slice(3);
    const filename = process.argv[2];
    const devMode = argOptions.includes('-d');
    const loadFromFile = argOptions.includes('-f');
    const devPort = argOptions.includes('-p') ? argOptions[argOptions.indexOf('-p') + 1] : '5050';

    if (!loadFromFile) {
        startLocalServer(devPort);
    }

    launchApp(getIndexURL(loadFromFile, filename, devPort), devMode);
};

init();
<commit_msg>allowing for specifying the local server port<commit_after>#!/usr/bin/env node

const getIndexURL = (loadFromFile, filename, localPort) => {

    const createFileURL = (filename) => {
        return `file://${__dirname}/${filename}`;
    };

    const createServerURL = (filename, localPort) => {
        const protocol = 'http';
        const domain = 'localhost';
        const port = `:${localPort}/`;

        return `${protocol}://${domain}${port}${filename}`;
    };

    if (loadFromFile) {
        return createFileURL(filename);
    }
    else {
        return createServerURL(filename, localPort);
    }
};

const startLocalServer = (localPort) => {
    require('child_process').exec(`cd ../../ && node_modules/scram-engine/node_modules/.bin/http-server -p ${localPort}`, (err, stdout, stderr) => {
        if (!err) {
            console.log(stdout);
        }
        else {
            console.log(stderr);
        }
    });
};

const launchApp = (indexURL, devMode) => {
    const electron = require('electron');
    const app = electron.app;
    const BrowserWindow = electron.BrowserWindow;
    const ipcMain = electron.ipcMain;

    app.commandLine.appendSwitch('--disable-http-cache');

    let mainWindow = null;

    app.on('ready', () => {
            mainWindow = new BrowserWindow({
                show: devMode
            });

            mainWindow.loadURL(indexURL);

            if (devMode) {
                mainWindow.webContents.openDevTools();
            }
    });

    ipcMain.on('asynchronous-message', (e, ...args) => {
        console.log(...args);
    });
};

const init = () => {
    const argOptions = process.argv.slice(3);
    const filename = process.argv[2];
    const devMode = argOptions.includes('-d');
    const loadFromFile = argOptions.includes('-f');
    const localPort = argOptions.includes('-p') ? argOptions[argOptions.indexOf('-p') + 1] : '5050';

    if (!loadFromFile) {
        startLocalServer(localPort);
    }

    launchApp(getIndexURL(loadFromFile, filename, localPort), devMode);
};

init();
<|endoftext|>


<commit_before>/**
 * NodeJS interface for the AfterShip API.
 * From docs here: https://www.aftership.com/docs/api/4.0
 *
 * @author Kirk Morales (kirk@intrakr.com), Bossa (github.com/BossaGroove)
 * @copyright Copyright (C) 2013
 * @license GNU General Public License, version 2 (see LICENSE.md)
 */

var _ = require('lodash');

/**
 * Hostname for AfterShip API.
 * @type {string}
 * @private
 */
var request_hostname = process.env.AFTERSHIP_NODEJS_SDK_HOST || 'api.aftership.com';

/**
 * Port for AfterShip API.
 * @type {number}
 * @private
 */
var request_post = process.env.AFTERSHIP_NODEJS_SDK_PORT || 443;

var transport = require((request_post === 443)?'https':'http');

/**
 * Path for AfterShip API.
 * @type {string}
 * @const
 * @private
 */
var API_PATH = '/v4';

/**
 * Initializes the AfterShip plugin.
 * @param {string} api_key - AfterShip api key
 * @return {Object.<string,function>}
 */
module.exports = function(api_key) {
	'use strict';

	// Require API key
	if (!api_key) {
		return {};
	}

	/**
	 * Performs an API request.
	 * @param {string} method The HTTP method.
	 * @param {string} path The HTTP path to append to the AfterShip default.
	 * @param {Object} data Body for POST requests.
	 * @param {function(?string, object=)} callback
	 * @private
	 */
	function request(method, path, data, callback) {

		// Make sure path starts with a slash
		if (path.substr(0, 1) !== '/') {
			path = '/' + path;
		}

		data = JSON.stringify(data);

		// console.log(data);

		var asReq = transport.request({
			hostname: request_hostname,
			port: request_post,
			path: API_PATH + path,
			method: method,
			headers: {
				'Content-Type': 'application/json',
				'Content-Length': data.length,
				'aftership-api-key': api_key
			}
		}, function(asRes) {
			var body = '';

			// Capture the response chunks
			asRes.on('data', function(chunk) {
				body += chunk;
			});

			// Called when request is complete
			asRes.on('end', function() {
				body = JSON.parse(body);

				if (!body || !body.meta) {
					callback('Could not parse response');
					return;
				}

				callback(null, body);
			});
		});

		// Capture any errors
		asReq.on('error', function(e) {
			callback(e.message);
		});

		asReq.write(data);
		asReq.end();
	}

	return {

		/**
		 * create a new tracking_number.
		 * @param {string} tracking_number - The number to track. Default: DHL.
		 * @param {Object|function(string, Array=)} params - Additional options to attach.
		 * @param {function(string, Array=)=} callback - callback function
		 */
		'createTracking': function(tracking_number, params, callback) {

			if (!callback) {
				callback = params;
				params = {};
			}

			if (!_.isString(tracking_number)) {
				callback('Missing Required Parameter: tracking number');
			}

			params.tracking_number = tracking_number;

			request('POST', '/trackings', {tracking: params}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 201) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the tracking number and data
				callback(null, body.data);
			});
		},

		/**
		 * Get a tracking number
		 * @param {string} slug - slug of the tracking number
		 * @param {string} tracking_number    - number to get
		 * @param {Array|string|function(string, Object=)} fields - Fields to return:
		 * https://www.aftership.com/docs/api/3.0/tracking/get-trackings-slug-tracking_number
		 * @param {function(string, Object=)=} callback - callback function
		 */
		'getTracking': function(slug, tracking_number, fields, callback) {

			if (!callback) {
				callback = fields;
				fields = [];
			}

			if (!_.isString(tracking_number)) {
				callback('Missing Required Parameter: tracking number');
			}

			if (!_.isString(slug)) {
				callback('Missing Required Parameter: tracking number');
			}

			if (Array.isArray(fields)) {
				fields = fields.join(',');
			}

			fields = 'fields=' + fields;

			request('GET', '/trackings/' + slug + '/' + tracking_number + '?' + fields, {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the time and checkpoints
				callback(null, body.data);
			});

			return null;
		},

		/**
		 * Gets all tracking numbers in account.
		 * @param {Object|function} options - Defined here:
		 * https://www.aftership.com/docs/api/3.0/tracking/get-trackings
		 * @param {function(string, Object=)=} callback
		 */
		'getTrackings': function(options, callback) {
			if (!callback) {
				callback = options;
				options = {};
			}

			request('GET', '/trackings', options, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || typeof body.data !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the time and checkpoints
				callback(null, body.data);
			});
		},

		/**
		 * Updates the tracking for an existing number
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {Array} options Fields to update:
		 *  https://www.aftership.com/docs/api/3.0/tracking/put-trackings-slug-tracking_number
		 * @param {function(string, Object=)} callback
		 */
		'updateTracking': function(slug, tracking_number, options, callback) {
			request('PUT', '/trackings/' + slug + '/' + tracking_number, {tracking: options}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Delete a specific tracking number.
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {function(string, Object=)} callback
		 */
		'deleteTracking': function(slug, tracking_number, callback) {
			request('DELETE', '/trackings/' + slug + '/' + tracking_number, {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the tracking number and slug
				callback(null, body.data);
			});
		},

		/**
		 * Get the last checkpoint information of a tracking number
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {Array|string|function(string, Object=)} fields Fields to update: https://www.aftership.com/docs/api/3.0/last_checkpoint
		 * @param {function(string, Object=)} callback
		 */
		'getLastCheckpoint': function(slug, tracking_number, fields, callback) {

			if (!callback) {
				callback = fields;
				fields = [];
			}

			if (Array.isArray(fields)) {
				fields = fields.join(',');
			}

			fields = 'fields=' + fields;

			request('GET', '/last_checkpoint/' + slug + '/' + tracking_number + '?' + fields, {}, function(err, body) {

				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.checkpoint || typeof body.data.checkpoint !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Gets all available couriers.
		 * @param {function(string, Object=)} callback
		 */
		'getCouriers': function(callback) {
			request('GET', '/couriers', {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || typeof body.data !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Detect the courier for given tracking number
		 * @param {string} tracking_number - tracking number to be detected
		 * @param {object|function(string, Object=)} required_fields - optional, hash of required fields
		 * possible values: {"tracking_account_number": "", "tracking_postal_code": "", "tracking_ship_date": ""}
		 * @param {string|function(string, Object=)=} detect_mode - optional, accept "strict" or "tracking_number"
		 * @param {function(string, Object=)=} callback - callback function
		 */
		'detectCouriers': function(tracking_number, required_fields, detect_mode, callback) {
			if (!callback) {
				callback = detect_mode;
				detect_mode = 'tracking_number';
			}

			if (!callback) {
				callback = required_fields;
				required_fields = {};
			}

			var param = {
				tracking_number: tracking_number,
				tracking_account_number: required_fields.tracking_account_number,
				tracking_postal_code: required_fields.tracking_postal_code,
				tracking_ship_date: required_fields.tracking_ship_date,
				detect_mode: detect_mode
			};

			request('POST', '/couriers/detect/', param, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.couriers || typeof body.data.couriers !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		}
	};
};

<commit_msg>nodejs sdk<commit_after>/**
 * NodeJS interface for the AfterShip API.
 * From docs here: https://www.aftership.com/docs/api/4.0
 *
 * @author Kirk Morales (kirk@intrakr.com), Bossa (github.com/BossaGroove)
 * @copyright Copyright (C) 2013
 * @license GNU General Public License, version 2 (see LICENSE.md)
 */

var _ = require('lodash');

/**
 * Hostname for AfterShip API.
 * @type {string}
 * @private
 */
var request_hostname = process.env.AFTERSHIP_NODEJS_SDK_HOST || 'api.aftership.com';

/**
 * Port for AfterShip API.
 * @type {number}
 * @private
 */
var request_post = process.env.AFTERSHIP_NODEJS_SDK_PORT || 443;

var transport = require((request_post === 443)?'https':'http');

/**
 * Path for AfterShip API.
 * @type {string}
 * @const
 * @private
 */
var API_PATH = '/v4';

/**
 * Initializes the AfterShip plugin.
 * @param {string} api_key - AfterShip api key
 * @return {Object.<string,function>}
 */
module.exports = function(api_key) {
	'use strict';

	// Require API key
	if (!api_key) {
		return {};
	}

	/**
	 * Performs an API request.
	 * @param {string} method The HTTP method.
	 * @param {string} path The HTTP path to append to the AfterShip default.
	 * @param {Object} data Body for POST requests.
	 * @param {function(?string, object=)} callback
	 * @private
	 */
	function request(method, path, data, callback) {

		// Make sure path starts with a slash
		if (path.substr(0, 1) !== '/') {
			path = '/' + path;
		}

		data = JSON.stringify(data);

		// console.log(data);

		var asReq = transport.request({
			hostname: request_hostname,
			port: request_post,
			path: API_PATH + path,
			method: method,
			headers: {
				'Content-Type': 'application/json',
				'Content-Length': data.length,
				'aftership-api-key': api_key
			}
		}, function(asRes) {
			var body = '';

			// Capture the response chunks
			asRes.on('data', function(chunk) {
				body += chunk;
			});

			// Called when request is complete
			asRes.on('end', function() {
				try {
					body = JSON.parse(body);

					if (!body || !body.meta) {
						callback('Could not parse response');
						return;
					}

					callback(null, body);
				} catch (e) {
					callback('Could not parse response');
				}
			});
		});

		// Capture any errors
		asReq.on('error', function(e) {
			callback(e.message);
		});

		asReq.write(data);
		asReq.end();
	}

	return {

		/**
		 * create a new tracking_number.
		 * @param {string} tracking_number - The number to track. Default: DHL.
		 * @param {Object|function(string, Array=)} params - Additional options to attach.
		 * @param {function(string, Array=)=} callback - callback function
		 */
		'createTracking': function(tracking_number, params, callback) {

			if (!callback) {
				callback = params;
				params = {};
			}

			if (!_.isString(tracking_number)) {
				callback('Missing Required Parameter: tracking number');
			}

			params.tracking_number = tracking_number;

			request('POST', '/trackings', {tracking: params}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 201) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the tracking number and data
				callback(null, body.data);
			});
		},

		/**
		 * Get a tracking number
		 * @param {string} slug - slug of the tracking number
		 * @param {string} tracking_number    - number to get
		 * @param {Array|string|function(string, Object=)} fields - Fields to return:
		 * https://www.aftership.com/docs/api/3.0/tracking/get-trackings-slug-tracking_number
		 * @param {function(string, Object=)=} callback - callback function
		 */
		'getTracking': function(slug, tracking_number, fields, callback) {

			if (!callback) {
				callback = fields;
				fields = [];
			}

			if (!_.isString(tracking_number)) {
				callback('Missing Required Parameter: tracking number');
			}

			if (!_.isString(slug)) {
				callback('Missing Required Parameter: tracking number');
			}

			if (Array.isArray(fields)) {
				fields = fields.join(',');
			}

			fields = 'fields=' + fields;

			request('GET', '/trackings/' + slug + '/' + tracking_number + '?' + fields, {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the time and checkpoints
				callback(null, body.data);
			});

			return null;
		},

		/**
		 * Gets all tracking numbers in account.
		 * @param {Object|function} options - Defined here:
		 * https://www.aftership.com/docs/api/3.0/tracking/get-trackings
		 * @param {function(string, Object=)=} callback
		 */
		'getTrackings': function(options, callback) {
			if (!callback) {
				callback = options;
				options = {};
			}

			request('GET', '/trackings', options, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || typeof body.data !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the time and checkpoints
				callback(null, body.data);
			});
		},

		/**
		 * Updates the tracking for an existing number
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {Array} options Fields to update:
		 *  https://www.aftership.com/docs/api/3.0/tracking/put-trackings-slug-tracking_number
		 * @param {function(string, Object=)} callback
		 */
		'updateTracking': function(slug, tracking_number, options, callback) {
			request('PUT', '/trackings/' + slug + '/' + tracking_number, {tracking: options}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Delete a specific tracking number.
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {function(string, Object=)} callback
		 */
		'deleteTracking': function(slug, tracking_number, callback) {
			request('DELETE', '/trackings/' + slug + '/' + tracking_number, {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.tracking || typeof body.data.tracking !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				// Return the tracking number and slug
				callback(null, body.data);
			});
		},

		/**
		 * Get the last checkpoint information of a tracking number
		 * @param {string} slug
		 * @param {string} tracking_number
		 * @param {Array|string|function(string, Object=)} fields Fields to update: https://www.aftership.com/docs/api/3.0/last_checkpoint
		 * @param {function(string, Object=)} callback
		 */
		'getLastCheckpoint': function(slug, tracking_number, fields, callback) {

			if (!callback) {
				callback = fields;
				fields = [];
			}

			if (Array.isArray(fields)) {
				fields = fields.join(',');
			}

			fields = 'fields=' + fields;

			request('GET', '/last_checkpoint/' + slug + '/' + tracking_number + '?' + fields, {}, function(err, body) {

				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.checkpoint || typeof body.data.checkpoint !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Gets all available couriers.
		 * @param {function(string, Object=)} callback
		 */
		'getCouriers': function(callback) {
			request('GET', '/couriers', {}, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || typeof body.data !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		},

		/**
		 * Detect the courier for given tracking number
		 * @param {string} tracking_number - tracking number to be detected
		 * @param {object|function(string, Object=)} required_fields - optional, hash of required fields
		 * possible values: {"tracking_account_number": "", "tracking_postal_code": "", "tracking_ship_date": ""}
		 * @param {string|function(string, Object=)=} detect_mode - optional, accept "strict" or "tracking_number"
		 * @param {function(string, Object=)=} callback - callback function
		 */
		'detectCouriers': function(tracking_number, required_fields, detect_mode, callback) {
			if (!callback) {
				callback = detect_mode;
				detect_mode = 'tracking_number';
			}

			if (!callback) {
				callback = required_fields;
				required_fields = {};
			}

			var param = {
				tracking_number: tracking_number,
				tracking_account_number: required_fields.tracking_account_number,
				tracking_postal_code: required_fields.tracking_postal_code,
				tracking_ship_date: required_fields.tracking_ship_date,
				detect_mode: detect_mode
			};

			request('POST', '/couriers/detect/', param, function(err, body) {
				if (err) {
					callback(err);
					return;
				}

				// Check for valid meta code
				if (!body.meta || !body.meta.code || body.meta.code !== 200) {
					callback(body.meta.code + ': ' + body.meta.message, body.data);
					return;
				}

				// Check for valid data contents
				if (!body.data || !body.data.couriers || typeof body.data.couriers !== 'object') {
					callback('500: Invalid response body');
					return;
				}

				callback(null, body.data);
			});
		}
	};
};

<|endoftext|>


<commit_before>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 1048, height: 720})

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`)

  // Open the DevTools.
  mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>Switch off devTools in main.js<commit_after>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 1048, height: 720})

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`)

  // Open the DevTools.
  //mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>


<commit_before>"use strict";
var TXT_LENGTH = 50;
var HINT_DELAY = 1500;

var hint = function() {
    var storedKeyDOM;
    var timer;

    var reset = function() {
        window.clearTimeout(timer);
        if (typeof(storedKeyDOM) !== "undefined") {
            storedKeyDOM.classList.remove("hint");
        }
    };

    var set =  function(keyDOM) {
        reset();
        storedKeyDOM = keyDOM;
        timer = window.setTimeout(function() {
            storedKeyDOM.classList.add("hint");
        }, HINT_DELAY);
    };

    return {
        reset: reset,
        set: set
    };
}();

function exercise() {
    var lessonString;
    var pos;
    var error;
    var timeStamp;
    var keyLogging = {};
    var rankedKeys = JSON.parse(localStorage.getItem("rankedKeys"));
    var orderdKeys = JSON.parse(localStorage.getItem("orderdKeys"));

    var txtDone = document.getElementById('txtDone');
    var txtNow = document.getElementById('txtNow');
    var txtNext = document.getElementById('txtNext');

    document.getElementById("exercise").classList.remove("hide");
    keyboard.setKeyboard(localStorage.keyboard);
    newLesson();

    window.onkeypress = function(event){
        checkInput(event);
    };

    function updateView() {
        txtDone.innerHTML = lessonString.substring(0, pos);
        txtNow.innerHTML = lessonString[pos] || "";
        txtNow.classList.remove("error");
        txtNext.innerHTML = lessonString.substring(pos+1, TXT_LENGTH);

        if (pos === 0) {
            var buffer = "";
            rankedKeys.forEach(function(item, _) {
                buffer += '';
                buffer += item.key;
                buffer += '';
            });
            document.getElementById("keysRanked").innerHTML = buffer;
            buffer = "";
            orderdKeys.forEach(function(item, _) {
                buffer += '';
                buffer += item;
                buffer += '';
            });
            document.getElementById("keysNext").innerHTML = buffer;
        }
    }

    function newLesson() {
        var space = 4 + Math.floor(Math.random() * 4);
        if (rankedKeys[rankedKeys.length-1].points > 1500 &&
            rankedKeys[rankedKeys.length-1].count > 40
           ) {
               rankedKeys.push({
                   "key": orderdKeys[0],
                   "points": 0,
                   "count": 0
               });
                orderdKeys.splice(0, 1);
                localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
                localStorage.setItem("orderdKeys", JSON.stringify(orderdKeys));
           }

        keyLogging = {};
        lessonString = "";
        pos = 0;
        for (var i = 0; i < TXT_LENGTH; i++) {
            if (i === space && i < TXT_LENGTH-2) {
                lessonString += " ";
                space += 4 + Math.floor(Math.random() * 4);
                continue;
            }
            var keyPos = Math.floor(Math.random() * rankedKeys.length);
            lessonString += orderdKeys[keyPos];
        }
        updateView();
        hint.set(keyboard.getDOM(lessonString[pos]));
    }

    function checkInput(event) {
        if (lessonString[pos] === String.fromCharCode(event.which)) {
            var timeStampEnd = new Date().getTime();
            if (pos !== 0) {
                var key = lessonString[pos];
                if (typeof(keyLogging[key]) === "undefined") {
                    keyLogging[key] = {count: 0, time: 0, err: 0};
                }
                keyLogging[key].count++;
                keyLogging[key].time += timeStampEnd - timeStamp;
                keyLogging[key].err += error;
            }
            pos++;
            if (pos < TXT_LENGTH) {
                hint.set(keyboard.getDOM(lessonString[pos]));
            } else {
                calculatePoints();
                hint.reset();
                newLesson();
            }
            updateView();
            timeStamp = new Date().getTime();
            error = false;
        } else {
            txtNow.classList.add("error");
            error = true;
        }
    }

    function calculatePoints() {
        rankedKeys.forEach(function(item, index) {
            var k = item.key;
            if (typeof(keyLogging[k]) === "undefined") {
                return;
            }
            var speed = 2000 - (keyLogging[k].time / keyLogging[k].count);
            if (speed < 0) {
                speed = 0;
            }
            var errors = (1 - (keyLogging[k].err / keyLogging[k].count));
            var points = (speed*errors);
            points += item.points * 2;
            points /= 3;
            rankedKeys[index].points = points.toFixed(2);
            rankedKeys[index].count += keyLogging[k].count;
        });
        rankedKeys.sort(function(a, b){
            return b.points - a.points;
        });
        localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
    }
}

var keyboard = function(){
    var keyDOMs = {};

    function setKeyboard(){
        document.getElementById("keyboard").innerHTML =
            keyboards[localStorage.keyboard].svg;
        var layout = keyboards[localStorage.keyboard].layout[localStorage.layout].mapping;
        for (var l in layout) {
            keyDOMs[l] = document.getElementById(layout[l]);
        }
    }

    function getDOM(key) {
        return keyDOMs[key];
    }

    return {
        setKeyboard: setKeyboard,
        getDOM: getDOM
    };
}();

function setup() {
    var setup = document.getElementById("setup");
    var saveConfig = document.getElementById("saveConfig");
    var selectKeyboard = document.getElementById("selectKeyboard");
    var selectLayout = document.getElementById("selectLayout");

    setup.classList.remove("hide");
    updateKeyboards();

    function updateKeyboards() {
        for (var k in keyboards) {
            var option = document.createElement("option");
            option.text = k;
            selectKeyboard.add(option);
        }
        selectKeyboard.selectedIndex = -1;
        selectKeyboard.onchange = function(event) {
            updateLayout(event.target.value);
            showKeyboardPreview(event.target.value);
        };
    }

    function showKeyboardPreview(keyboardType) {
        document.getElementById("preview").innerHTML = keyboards[keyboardType].svg;
    }

    function updateLayout(keyboardType) {
        selectLayout.innerHTML = "";
        for (var l in keyboards[keyboardType].layout) {
            var option = document.createElement("option");
            option.text = l;
            selectLayout.add(option);
        }
        selectLayout.selectedIndex = -1;
    }

    function createOrder() {
        var orderdKeys = [];
        var layout = keyboards[localStorage.keyboard].layout[localStorage.layout];
        var order = keyboards[localStorage.keyboard].ordering;
        for (var o = 0; o < order.length; o++) {
            for (var l in layout.mapping) {
                if (layout.mapping[l] === order[o] &&
                    language[layout.language].indexOf(l) !== -1
                   ) {
                       orderdKeys.push(l);
                   }
            }
        }
        var rankedKeys = [];
        for (var i = 0; i < 4; i++) {
            rankedKeys.push({
                "key": orderdKeys[0],
                "points": 0,
                "count": 0
            });
            orderdKeys.splice(0, 1);
        }
        localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
        localStorage.setItem("orderdKeys", JSON.stringify(orderdKeys));
    }

    function save() {
        if (
            selectKeyboard.selectedIndex === -1 ||
            selectLayout.selectedIndex === -1
        ) {
            alert("You have to select a keyboard design and a layout");
            return;
        }

        localStorage.keyboard = selectKeyboard.value;
        localStorage.layout = selectLayout.value;
        createOrder();
        setup.classList.add("hide");
        exercise();
    }

    saveConfig.onclick = function() {
        save();
    };
    saveConfig.onkeydown = function(event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            save();
        }
    };
}

function showWelcome() {
    var welcome = document.getElementById("welcome");
    var startSetup = document.getElementById("startSetup");

    welcome.classList.remove("hide");
    startSetup.onclick = function() {
        welcome.classList.add("hide");
        setup();
    };
    startSetup.onkeydown = function(event) {
        if (event.keyCode === 13) {
            welcome.classList.add("hide");
            setup();
        }
    };
}

function setKey(key) {
    console.log(key);
}

function init() {
    //window.localStorage.clear();

    // drop backspace
    document.addEventListener("keydown", function(event) {
        if (event.which === 8) {
           event.preventDefault();
        }
    });

    if (
        typeof(localStorage.keyboard) === "undefined" ||
        typeof(localStorage.layout) === "undefined" ||
        typeof(localStorage.orderdKeys) === "undefined" ||
        typeof(localStorage.rankedKeys) === "undefined"
    ) {
        showWelcome();
    } else {
        exercise();
    }
}
<commit_msg>removed small bug, while new key added part 4<commit_after>"use strict";
var TXT_LENGTH = 50;
var HINT_DELAY = 1500;

var hint = function() {
    var storedKeyDOM;
    var timer;

    var reset = function() {
        window.clearTimeout(timer);
        if (typeof(storedKeyDOM) !== "undefined") {
            storedKeyDOM.classList.remove("hint");
        }
    };

    var set =  function(keyDOM) {
        reset();
        storedKeyDOM = keyDOM;
        timer = window.setTimeout(function() {
            storedKeyDOM.classList.add("hint");
        }, HINT_DELAY);
    };

    return {
        reset: reset,
        set: set
    };
}();

function exercise() {
    var lessonString;
    var pos;
    var error;
    var timeStamp;
    var keyLogging = {};
    var rankedKeys = JSON.parse(localStorage.getItem("rankedKeys"));
    var orderdKeys = JSON.parse(localStorage.getItem("orderdKeys"));

    var txtDone = document.getElementById('txtDone');
    var txtNow = document.getElementById('txtNow');
    var txtNext = document.getElementById('txtNext');

    document.getElementById("exercise").classList.remove("hide");
    keyboard.setKeyboard(localStorage.keyboard);
    newLesson();

    window.onkeypress = function(event){
        checkInput(event);
    };

    function updateView() {
        txtDone.innerHTML = lessonString.substring(0, pos);
        txtNow.innerHTML = lessonString[pos] || "";
        txtNow.classList.remove("error");
        txtNext.innerHTML = lessonString.substring(pos+1, TXT_LENGTH);

        if (pos === 0) {
            var buffer = "";
            rankedKeys.forEach(function(item, _) {
                buffer += '';
                buffer += item.key;
                buffer += '';
            });
            document.getElementById("keysRanked").innerHTML = buffer;
            buffer = "";
            orderdKeys.forEach(function(item, _) {
                buffer += '';
                buffer += item;
                buffer += '';
            });
            document.getElementById("keysNext").innerHTML = buffer;
        }
    }

    function newLesson() {
        var space = 4 + Math.floor(Math.random() * 4);
        if (rankedKeys[rankedKeys.length-1].points > 1500 &&
            rankedKeys[rankedKeys.length-1].count > 40
           ) {
               rankedKeys.push({
                   "key": orderdKeys[0],
                   "points": 0,
                   "count": 0
               });
                orderdKeys.splice(0, 1);
                localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
                localStorage.setItem("orderdKeys", JSON.stringify(orderdKeys));
           }

        keyLogging = {};
        lessonString = "";
        pos = 0;
        for (var i = 0; i < TXT_LENGTH; i++) {
            if (i === space && i < TXT_LENGTH-2) {
                lessonString += " ";
                space += 4 + Math.floor(Math.random() * 4);
                continue;
            }
            var keyPos = Math.floor(Math.random() * rankedKeys.length);
            lessonString += rankedKeys[keyPos].key;
        }
        updateView();
        hint.set(keyboard.getDOM(lessonString[pos]));
    }

    function checkInput(event) {
        if (lessonString[pos] === String.fromCharCode(event.which)) {
            var timeStampEnd = new Date().getTime();
            if (pos !== 0) {
                var key = lessonString[pos];
                if (typeof(keyLogging[key]) === "undefined") {
                    keyLogging[key] = {count: 0, time: 0, err: 0};
                }
                keyLogging[key].count++;
                keyLogging[key].time += timeStampEnd - timeStamp;
                keyLogging[key].err += error;
            }
            pos++;
            if (pos < TXT_LENGTH) {
                hint.set(keyboard.getDOM(lessonString[pos]));
            } else {
                calculatePoints();
                hint.reset();
                newLesson();
            }
            updateView();
            timeStamp = new Date().getTime();
            error = false;
        } else {
            txtNow.classList.add("error");
            error = true;
        }
    }

    function calculatePoints() {
        rankedKeys.forEach(function(item, index) {
            var k = item.key;
            if (typeof(keyLogging[k]) === "undefined") {
                return;
            }
            var speed = 2000 - (keyLogging[k].time / keyLogging[k].count);
            if (speed < 0) {
                speed = 0;
            }
            var errors = (1 - (keyLogging[k].err / keyLogging[k].count));
            var points = (speed*errors);
            points += item.points * 2;
            points /= 3;
            rankedKeys[index].points = points.toFixed(2);
            rankedKeys[index].count += keyLogging[k].count;
        });
        rankedKeys.sort(function(a, b){
            return b.points - a.points;
        });
        localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
    }
}

var keyboard = function(){
    var keyDOMs = {};

    function setKeyboard(){
        document.getElementById("keyboard").innerHTML =
            keyboards[localStorage.keyboard].svg;
        var layout = keyboards[localStorage.keyboard].layout[localStorage.layout].mapping;
        for (var l in layout) {
            keyDOMs[l] = document.getElementById(layout[l]);
        }
    }

    function getDOM(key) {
        return keyDOMs[key];
    }

    return {
        setKeyboard: setKeyboard,
        getDOM: getDOM
    };
}();

function setup() {
    var setup = document.getElementById("setup");
    var saveConfig = document.getElementById("saveConfig");
    var selectKeyboard = document.getElementById("selectKeyboard");
    var selectLayout = document.getElementById("selectLayout");

    setup.classList.remove("hide");
    updateKeyboards();

    function updateKeyboards() {
        for (var k in keyboards) {
            var option = document.createElement("option");
            option.text = k;
            selectKeyboard.add(option);
        }
        selectKeyboard.selectedIndex = -1;
        selectKeyboard.onchange = function(event) {
            updateLayout(event.target.value);
            showKeyboardPreview(event.target.value);
        };
    }

    function showKeyboardPreview(keyboardType) {
        document.getElementById("preview").innerHTML = keyboards[keyboardType].svg;
    }

    function updateLayout(keyboardType) {
        selectLayout.innerHTML = "";
        for (var l in keyboards[keyboardType].layout) {
            var option = document.createElement("option");
            option.text = l;
            selectLayout.add(option);
        }
        selectLayout.selectedIndex = -1;
    }

    function createOrder() {
        var orderdKeys = [];
        var layout = keyboards[localStorage.keyboard].layout[localStorage.layout];
        var order = keyboards[localStorage.keyboard].ordering;
        for (var o = 0; o < order.length; o++) {
            for (var l in layout.mapping) {
                if (layout.mapping[l] === order[o] &&
                    language[layout.language].indexOf(l) !== -1
                   ) {
                       orderdKeys.push(l);
                   }
            }
        }
        var rankedKeys = [];
        for (var i = 0; i < 4; i++) {
            rankedKeys.push({
                "key": orderdKeys[0],
                "points": 0,
                "count": 0
            });
            orderdKeys.splice(0, 1);
        }
        localStorage.setItem("rankedKeys", JSON.stringify(rankedKeys));
        localStorage.setItem("orderdKeys", JSON.stringify(orderdKeys));
    }

    function save() {
        if (
            selectKeyboard.selectedIndex === -1 ||
            selectLayout.selectedIndex === -1
        ) {
            alert("You have to select a keyboard design and a layout");
            return;
        }

        localStorage.keyboard = selectKeyboard.value;
        localStorage.layout = selectLayout.value;
        createOrder();
        setup.classList.add("hide");
        exercise();
    }

    saveConfig.onclick = function() {
        save();
    };
    saveConfig.onkeydown = function(event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            save();
        }
    };
}

function showWelcome() {
    var welcome = document.getElementById("welcome");
    var startSetup = document.getElementById("startSetup");

    welcome.classList.remove("hide");
    startSetup.onclick = function() {
        welcome.classList.add("hide");
        setup();
    };
    startSetup.onkeydown = function(event) {
        if (event.keyCode === 13) {
            welcome.classList.add("hide");
            setup();
        }
    };
}

function setKey(key) {
    console.log(key);
}

function init() {
    //window.localStorage.clear();

    // drop backspace
    document.addEventListener("keydown", function(event) {
        if (event.which === 8) {
           event.preventDefault();
        }
    });

    if (
        typeof(localStorage.keyboard) === "undefined" ||
        typeof(localStorage.layout) === "undefined" ||
        typeof(localStorage.orderdKeys) === "undefined" ||
        typeof(localStorage.rankedKeys) === "undefined"
    ) {
        showWelcome();
    } else {
        exercise();
    }
}
<|endoftext|>


<commit_before>var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;

app.on('window-all-closed', function() {
    app.quit();
});

app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 720, height: 400});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});
<commit_msg>Make main window slightly larger<commit_after>var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;

app.on('window-all-closed', function() {
    app.quit();
});

app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 750, height: 450});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});
<|endoftext|>


<commit_before>var Cylon = require('cylon');

Cylon.robot({
  connection: { name: 'sphero', adaptor: 'sphero', port: '/dev/tty.Sphero-RRP-AMP-SPP' },
  device: { name: 'sphero', driver: 'sphero' },

  work: function(my) {
    var on = false;
    every((1).second(), function() {
      // flash light
      if (on) {
        my.sphero.setColor('purple');
        on = false;
      } else {
        my.sphero.setColor('white');
        on = true;
      }
      my.sphero.roll(60, Math.floor(Math.random() * 360));
    });
  }
}).start();<commit_msg>Sphero matches leap<commit_after>var Cylon = require('cylon'),
    leap = require('./leap');

Cylon.robot({
  connection: { name: 'sphero', adaptor: 'sphero', port: '/dev/tty.Sphero-RRP-AMP-SPP' },
  device: { name: 'sphero', driver: 'sphero' },

  work: function(my) {
    var colors = ['purple', 'white', 'blue', 'red', 'green'];
    every((1).second(), function() {
      my.sphero.setColor(colors[leap.height];
      var direction = 0, offset = 1;
      if(leap.left) {
        direction += 3;
        offset = -1;
      }
      if(!leap.forward) {
        direction += offset;
      }
      direction = 90 * direction + 45;
      my.sphero.roll(60, direction);
    });
  }
}).start();
<|endoftext|>


<commit_before>//
// required packages
//
var fs = require('fs');
var winston = require('winston');
var express = require('express');
var request = require('request');
var cheerio = require('cheerio');
var forecasts = require('./forecasts.js');
var observations = require('./observations.js');

runServer();

function runServer() {

    configureLogger();

    initializeForecastProcessing();

    startHTTPServer();
}

function configureLogger() {

    var localLogFilePath = '/tmp/aviforecast-log.txt';

    // remove the default transport, so that we can reconfigure it
    winston.remove(winston.transports.Console);

    // NOTE verbose, info, warn, error are the log levels we're using
    winston.add(winston.transports.Console,
        {
            level:'info',
            timestamp:!(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') // the heroku envs already have timestamps
        });

    if (!(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging')) {
        winston.add(winston.transports.File, {level:'info', timestamp:true, json:false, filename:localLogFilePath});
        winston.info('main_configureLogger NOT production or staging mode; local logfile is at: ' + localLogFilePath);
    }
}

function initializeForecastProcessing() {

    var regions = JSON.parse(fs.readFileSync(forecasts.REGIONS_PATH, 'utf8'));

    // generate the forecast content
    forecasts.aggregateForecasts(regions);

    // configure a timer to regenerate the forecast content on a recurring basis
    setInterval(forecasts.aggregateForecasts, forecasts.FORECAST_GEN_INTERVAL_SECONDS * 1000, regions);
}

function startHTTPServer() {

    var app = express();

    // set up the express middleware, in the order we want it to execute

    // enable web server logging; pipe those log messages through winston
    var winstonStream = {
        write: function(str){
            winston.info(str);
        }
    };
    app.use(express.logger({stream:winstonStream}));

    // compress responses
    app.use(express.compress());

    // parse request bodies, including file uploads
    app.use(express.bodyParser({keepExtensions: true}));

    // support observation uploads
    app.post(app, '/v1/observation', function (req, res) {

        var observation = {
            providerId: req.params.providerId,
            email: req.params.email,
            notes: req.params.notes,
            image: req.files.image
        };

        winston.info('received observation; contents: ' + JSON.stringify(observation));

        if (!observation.providerId || !observation.email) {
            // bad request
            res.send(400);
        } else {
            observations.processObservation(observation, function(error) {
                if (!error) {
                    res.send(200);
                } else {
                    res.send(500);
                }
            });
        }
    });

    //
    // BEGIN PROXYING HACK
    //

    // BUGBUG hack to get around problem caching IPAC web pages on android due to cache-control:no-store header;
    // proxy their content to avoid these headers being sent to the client
    app.get('/proxy/ipac/:zone', function(req, res) {
        var baseUrl = 'http://www.idahopanhandleavalanche.org/';
        var url = baseUrl + req.params.zone + '.html';
        proxy(url, baseUrl, res);
    });

    // BUGBUG hack to get around problem caching CAIC web pages on android due to cache-control:no-store header;
    // proxy their content to avoid these headers being sent to the client
    app.get('/proxy/caic/:zone', function(req, res) {
        var baseUrl = 'http://avalanche.state.co.us/';
        var url = baseUrl + 'caic/pub_bc_avo.php?zone_id=' + req.params.zone;
        proxy(url, baseUrl, res);
    });

    function proxy(url, baseUrl, res) {
        request({url:url, jar:false, timeout: forecasts.DATA_REQUEST_TIMEOUT_SECONDS * 1000},
            function(error, response, body) {
                if (!error && response.statusCode === 200) {
                    winston.info('successful proxy response; url: ' + url);
                    body = fixRelativeLinks(baseUrl, body);
                    res.send(body);
                } else {
                    winston.warn('failed proxy response; url: ' + url + '; response status code: ' + (response ? response.statusCode : '[no response]') + '; error: ' + error);
                    res.send(503);
                }
            }
        );
    }

    function fixRelativeLinks(baseUrl, body) {
        // NOTE some sites use relative links, but don't specify the base url, which breaks things when
        // going through our explicit proxy; so fix the pages by putting in a <base> tag
        var $ = cheerio.load(body, {lowerCaseTags:true, lowerCaseAttributeNames:true});
        $('head').prepend('<base href=' + baseUrl + '>');
        return $.html();
    }

    //
    // END PROXYING HACK
    //

    // serve static content
    app.use(express.static(forecasts.STATIC_FILES_DIR_PATH, {maxAge: forecasts.CACHE_MAX_AGE_SECONDS * 1000 }));

    // handle errors gracefully
    app.use(express.errorHandler());

    // use the value from the PORT env variable if available, fallback if not
    var port = process.env.PORT || 5000;

    app.listen(port,
        function () {
            // NOTE if you don't get this log message, then the http server didn't start correctly;
            // check if another instance is already running...
            winston.info('HTTP server listening on port: ' + port);
        }
    );
}

<commit_msg>fix param bug<commit_after>//
// required packages
//
var fs = require('fs');
var winston = require('winston');
var express = require('express');
var request = require('request');
var cheerio = require('cheerio');
var forecasts = require('./forecasts.js');
var observations = require('./observations.js');

runServer();

function runServer() {

    configureLogger();

    initializeForecastProcessing();

    startHTTPServer();
}

function configureLogger() {

    var localLogFilePath = '/tmp/aviforecast-log.txt';

    // remove the default transport, so that we can reconfigure it
    winston.remove(winston.transports.Console);

    // NOTE verbose, info, warn, error are the log levels we're using
    winston.add(winston.transports.Console,
        {
            level:'info',
            timestamp:!(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') // the heroku envs already have timestamps
        });

    if (!(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging')) {
        winston.add(winston.transports.File, {level:'info', timestamp:true, json:false, filename:localLogFilePath});
        winston.info('main_configureLogger NOT production or staging mode; local logfile is at: ' + localLogFilePath);
    }
}

function initializeForecastProcessing() {

    var regions = JSON.parse(fs.readFileSync(forecasts.REGIONS_PATH, 'utf8'));

    // generate the forecast content
    forecasts.aggregateForecasts(regions);

    // configure a timer to regenerate the forecast content on a recurring basis
    setInterval(forecasts.aggregateForecasts, forecasts.FORECAST_GEN_INTERVAL_SECONDS * 1000, regions);
}

function startHTTPServer() {

    var app = express();

    // set up the express middleware, in the order we want it to execute

    // enable web server logging; pipe those log messages through winston
    var winstonStream = {
        write: function(str){
            winston.info(str);
        }
    };
    app.use(express.logger({stream:winstonStream}));

    // compress responses
    app.use(express.compress());

    // parse request bodies, including file uploads
    app.use(express.bodyParser({keepExtensions: true}));

    // support observation uploads
    app.post('/v1/observation', function (req, res) {

        var observation = {
            providerId: req.params.providerId,
            email: req.params.email,
            notes: req.params.notes,
            image: req.files.image
        };

        winston.info('received observation; contents: ' + JSON.stringify(observation));

        if (!observation.providerId || !observation.email) {
            // bad request
            res.send(400);
        } else {
            observations.processObservation(observation, function(error) {
                if (!error) {
                    res.send(200);
                } else {
                    res.send(500);
                }
            });
        }
    });

    //
    // BEGIN PROXYING HACK
    //

    // BUGBUG hack to get around problem caching IPAC web pages on android due to cache-control:no-store header;
    // proxy their content to avoid these headers being sent to the client
    app.get('/proxy/ipac/:zone', function(req, res) {
        var baseUrl = 'http://www.idahopanhandleavalanche.org/';
        var url = baseUrl + req.params.zone + '.html';
        proxy(url, baseUrl, res);
    });

    // BUGBUG hack to get around problem caching CAIC web pages on android due to cache-control:no-store header;
    // proxy their content to avoid these headers being sent to the client
    app.get('/proxy/caic/:zone', function(req, res) {
        var baseUrl = 'http://avalanche.state.co.us/';
        var url = baseUrl + 'caic/pub_bc_avo.php?zone_id=' + req.params.zone;
        proxy(url, baseUrl, res);
    });

    function proxy(url, baseUrl, res) {
        request({url:url, jar:false, timeout: forecasts.DATA_REQUEST_TIMEOUT_SECONDS * 1000},
            function(error, response, body) {
                if (!error && response.statusCode === 200) {
                    winston.info('successful proxy response; url: ' + url);
                    body = fixRelativeLinks(baseUrl, body);
                    res.send(body);
                } else {
                    winston.warn('failed proxy response; url: ' + url + '; response status code: ' + (response ? response.statusCode : '[no response]') + '; error: ' + error);
                    res.send(503);
                }
            }
        );
    }

    function fixRelativeLinks(baseUrl, body) {
        // NOTE some sites use relative links, but don't specify the base url, which breaks things when
        // going through our explicit proxy; so fix the pages by putting in a <base> tag
        var $ = cheerio.load(body, {lowerCaseTags:true, lowerCaseAttributeNames:true});
        $('head').prepend('<base href=' + baseUrl + '>');
        return $.html();
    }

    //
    // END PROXYING HACK
    //

    // serve static content
    app.use(express.static(forecasts.STATIC_FILES_DIR_PATH, {maxAge: forecasts.CACHE_MAX_AGE_SECONDS * 1000 }));

    // handle errors gracefully
    app.use(express.errorHandler());

    // use the value from the PORT env variable if available, fallback if not
    var port = process.env.PORT || 5000;

    app.listen(port,
        function () {
            // NOTE if you don't get this log message, then the http server didn't start correctly;
            // check if another instance is already running...
            winston.info('HTTP server listening on port: ' + port);
        }
    );
}

<|endoftext|>


<commit_before>var amdOptimize = require('amd-optimize');
var fs = require('fs');
var glob = require('glob');
var gulp = require('gulp');
var clean = require('gulp-clean');
var concat = require('gulp-concat');
var eventStream = require('event-stream');
var gulpFile = require('gulp-file');
var ignore = require('gulp-ignore');
var insert = require("gulp-insert");
var install = require("gulp-install");
var order = require('gulp-order');
var minifyCSS = require('gulp-minify-css');
var rename = require('gulp-rename');
var stylus = require('gulp-stylus');
var uglify = require('gulp-uglify');
var gulpUtil = require('gulp-util');
var webserver = require('gulp-webserver');
var mergeStream = require('merge-stream');
var path = require('path');
var requireDir = require('require-dir');
var _ = require('underscore');
var argv = require('yargs').argv;

var config = require('../../config');
var nunjucksBuild = require('./plugins/nunjucks-build');
var imgurlsAbsolutify = require('./plugins/imgurls-absolutify');
var imgurlsCachebust = require('./plugins/imgurls-cachebust');
var imgurlsParse = require('./plugins/imgurls-parse');
var paths = require('./paths');

requireDir('tasks');

gulp.task('install', function(done) {
    // Bumps bower and npm dependencies.
    gulp.src(['bower.json', 'package.json'])
        .pipe(install())
        .pipe(gulpUtil.noop())  // Wait for dependencies to finish installing.
        .on('finish', function() {
            done();
        });
});

gulp.task('bower_copy', ['install'], function() {
    // Copy files from Bower into project.
    return _.each(Object.keys(config.bowerConfig), function(source) {
        var dest = config.bowerConfig[source];
        gulp.src(paths.bower + source)
            .pipe(gulp.dest(dest));
    });
});

gulp.task('require_config', ['install'], function() {
    // Build a require.js file that contains a convenience call to
    // require.config that sets up some pre-known paths.
    return gulp.src(paths.require)
        .pipe(insert.append(config.inlineRequireConfig))
        .pipe(gulp.dest(config.LIB_DEST_PATH));
});

gulp.task('templates_build', function() {
    // Build Nunjucks templates into a templates.js file.
    return gulp.src(paths.html)
        .pipe(nunjucksBuild())
        .pipe(concat('templates.js'))
        .pipe(insert.prepend(
            '(function() {\n' +
            'var templates = {};\n'))
        .pipe(insert.append(
            'define("templates", ["nunjucks", "helpers"], function(nunjucks) {\n' +
            '    nunjucks.env = new nunjucks.Environment([], {autoescape: true});\n' +
            '    nunjucks.env.cache = nunjucks.templates = templates;\n' +
            '    console.log("Templates loaded");\n' +
            '    return nunjucks;\n' +
            '});\n' +
            '})();'
        ))
        .pipe(gulp.dest('src'));
});

gulp.task('css_compile', function() {
    // Compile .styl files into .styl.css files.
    return gulp.src(paths.styl)
        .pipe(stylus())
        .pipe(rename(function(path) {
            path.extname = '.styl.css';
        }))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('css_bundles', ['css_compile'], function() {
    // Read the config and build specified CSS bundles (like for splash.css).
    var streams = [];

    Object.keys(config.cssBundles || []).forEach(function(bundle) {
        streams.push(gulp.src(config.CSS_DEST_PATH + config.cssBundles[bundle])
            .pipe(concat(bundle))
            .pipe(minifyCSS())
            .pipe(gulp.dest(config.CSS_DEST_PATH))
        );
    });

    // Yes, cross the streams.
    if (streams) {
        return mergeStream.apply(this, streams);
    }
});

gulp.task('css_build', ['css_bundles', 'css_compile'], function() {
    // Bundle and minify all the CSS into include.css.
    var excludes = Object.keys(config.cssBundles || []).map(function(bundle) {
        // Exclude generated bundles if any specified in the config.
        return bundle;
    });
    // Exclude previously generated builds.
    excludes.push(paths.include_css);
    // Exclude from project config.
    if (config.cssExcludes) {
        excludes = excludes.concat(config.cssExcludes);
    }
    excludes = excludes.map(function(css) { return '!' + config.CSS_DEST_PATH + css; });

    // Determine which CSS files and which order to concat through index.html.
    var css_files = [];
    var data = fs.readFileSync(path.resolve('src', 'index.html'));
    data = data.toString();
    var css_pattern = new RegExp('href="/media/css/(.+.css)"', 'g');
    while (match = css_pattern.exec(data)) {
        css_files.push(match[1]);
    }
    css_src = css_files.map(function(css) {
        return config.CSS_DEST_PATH + css;
    });

    return gulp.src(css_src.concat(excludes))
        .pipe(stylus({compress: true}))
        .pipe(imgurlsCachebust())
        .pipe(minifyCSS())
        .pipe(order(css_files,
                    {base: config.CSS_DEST_PATH}))
        .pipe(concat(paths.include_css))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('imgurls_write', ['css_build'], function() {
    // imgurls.txt is a list of cachebusted img URLs that is used by Zamboni
    // to generate the appcache manifest.
    return gulp.src(config.CSS_DEST_PATH + paths.include_css)
        .pipe(imgurlsAbsolutify())
        .pipe(imgurlsParse())
        .pipe(rename('imgurls.txt'))
        .pipe(gulp.dest('src/media'));
});

gulp.task('buildID_write', function() {
    // Writes build ID to src/media/build_id.txt.
    var buildID = new Date().getTime().toString();
    return gulpFile('build_id.txt', buildID)
        .pipe(gulp.dest('src/media'));
});

gulp.task('js_build', ['templates_build'], function() {
    // Uses the AMD optimizer to bundle our JS modules.
    // Will read our RequireJS config to handle shims, paths, and name
    // anonymous modules.
    // Traces all modules and outputs them in the correct order.
    return eventStream.merge(
        // Almond loader.
        gulp.src(paths.almond),
        // JS bundle.
        gulp.src(paths.js)
            .pipe(amdOptimize('main', {
                baseUrl: config.JS_DEST_PATH,
                findNestedDependencies: true,
                paths: config.requireConfig.paths,
                shim: config.requireConfig.shim,
                wrapShim: true
            }))
            .pipe(concat(paths.include_js)),
        // Init script.
        gulp.src(paths.init)
    )
        .pipe(order(['**/almond.js', '**/include.js', '**/init.js']))
        .pipe(uglify())
        .pipe(concat(paths.include_js))
        .pipe(gulp.dest(config.JS_DEST_PATH));
});

gulp.task('serve', ['css_compile', 'templates_build'], function() {
    // template -- template to serve (e.g., index (default), app, server).
    // port -- server port, defaults to config port or 8675.
    return gulp.src(['src'])
        .pipe(webserver({
            fallback: argv.template || 'index' + '.html',
            livereload: true,
            port: argv.port || config.PORT || 8675
        }));
});

gulp.task('clean', function() {
    return gulp.src([
        config.CSS_DEST_PATH + 'splash.css',
        config.CSS_DEST_PATH + paths.include_css,
        config.JS_DEST_PATH + paths.include_js,
        paths.styl_compiled,
        '_tmp',
        'src/locales',
        'src/media/locales',
        'src/media/build_id.txt',
        'src/media/imgurls.txt',
        'src/templates.js'
    ], {read: false})
        .pipe(clean({force: true}));
});

gulp.task('watch', function() {
    // Watch and recompile on change.
    // Note: does not detect new and deleted files while running.
    gulp.watch(paths.html, ['templates_build']);
    gulp.watch(paths.styl, ['css_compile']);
});

gulp.task('default', ['watch', 'serve']);
gulp.task('update', ['bower_copy', 'require_config']);
gulp.task('build', ['buildID_write', 'css_build', 'js_build',
                    'templates_build', 'imgurls_write']);

module.exports = {
    paths: paths
};
<commit_msg>remove underscore dep<commit_after>var amdOptimize = require('amd-optimize');
var fs = require('fs');
var glob = require('glob');
var gulp = require('gulp');
var clean = require('gulp-clean');
var concat = require('gulp-concat');
var eventStream = require('event-stream');
var gulpFile = require('gulp-file');
var ignore = require('gulp-ignore');
var insert = require("gulp-insert");
var install = require("gulp-install");
var order = require('gulp-order');
var minifyCSS = require('gulp-minify-css');
var rename = require('gulp-rename');
var stylus = require('gulp-stylus');
var uglify = require('gulp-uglify');
var gulpUtil = require('gulp-util');
var webserver = require('gulp-webserver');
var mergeStream = require('merge-stream');
var path = require('path');
var requireDir = require('require-dir');
var _ = require('underscore');
var argv = require('yargs').argv;

var config = require('../../config');
var nunjucksBuild = require('./plugins/nunjucks-build');
var imgurlsAbsolutify = require('./plugins/imgurls-absolutify');
var imgurlsCachebust = require('./plugins/imgurls-cachebust');
var imgurlsParse = require('./plugins/imgurls-parse');
var paths = require('./paths');

requireDir('tasks');

gulp.task('install', function(done) {
    // Bumps bower and npm dependencies.
    gulp.src(['bower.json', 'package.json'])
        .pipe(install())
        .pipe(gulpUtil.noop())  // Wait for dependencies to finish installing.
        .on('finish', function() {
            done();
        });
});

gulp.task('bower_copy', ['install'], function() {
    // Copy files from Bower into project.
    return Object.keys(config.bowerConfig).forEach(function(source) {
        var dest = config.bowerConfig[source];
        gulp.src(paths.bower + source)
            .pipe(gulp.dest(dest));
    });
});

gulp.task('require_config', ['install'], function() {
    // Build a require.js file that contains a convenience call to
    // require.config that sets up some pre-known paths.
    return gulp.src(paths.require)
        .pipe(insert.append(config.inlineRequireConfig))
        .pipe(gulp.dest(config.LIB_DEST_PATH));
});

gulp.task('templates_build', function() {
    // Build Nunjucks templates into a templates.js file.
    return gulp.src(paths.html)
        .pipe(nunjucksBuild())
        .pipe(concat('templates.js'))
        .pipe(insert.prepend(
            '(function() {\n' +
            'var templates = {};\n'))
        .pipe(insert.append(
            'define("templates", ["nunjucks", "helpers"], function(nunjucks) {\n' +
            '    nunjucks.env = new nunjucks.Environment([], {autoescape: true});\n' +
            '    nunjucks.env.cache = nunjucks.templates = templates;\n' +
            '    console.log("Templates loaded");\n' +
            '    return nunjucks;\n' +
            '});\n' +
            '})();'
        ))
        .pipe(gulp.dest('src'));
});

gulp.task('css_compile', function() {
    // Compile .styl files into .styl.css files.
    return gulp.src(paths.styl)
        .pipe(stylus())
        .pipe(rename(function(path) {
            path.extname = '.styl.css';
        }))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('css_bundles', ['css_compile'], function() {
    // Read the config and build specified CSS bundles (like for splash.css).
    var streams = [];

    Object.keys(config.cssBundles || []).forEach(function(bundle) {
        streams.push(gulp.src(config.CSS_DEST_PATH + config.cssBundles[bundle])
            .pipe(concat(bundle))
            .pipe(minifyCSS())
            .pipe(gulp.dest(config.CSS_DEST_PATH))
        );
    });

    // Yes, cross the streams.
    if (streams) {
        return mergeStream.apply(this, streams);
    }
});

gulp.task('css_build', ['css_bundles', 'css_compile'], function() {
    // Bundle and minify all the CSS into include.css.
    var excludes = Object.keys(config.cssBundles || []).map(function(bundle) {
        // Exclude generated bundles if any specified in the config.
        return bundle;
    });
    // Exclude previously generated builds.
    excludes.push(paths.include_css);
    // Exclude from project config.
    if (config.cssExcludes) {
        excludes = excludes.concat(config.cssExcludes);
    }
    excludes = excludes.map(function(css) { return '!' + config.CSS_DEST_PATH + css; });

    // Determine which CSS files and which order to concat through index.html.
    var css_files = [];
    var data = fs.readFileSync(path.resolve('src', 'index.html'));
    data = data.toString();
    var css_pattern = new RegExp('href="/media/css/(.+.css)"', 'g');
    while (match = css_pattern.exec(data)) {
        css_files.push(match[1]);
    }
    css_src = css_files.map(function(css) {
        return config.CSS_DEST_PATH + css;
    });

    return gulp.src(css_src.concat(excludes))
        .pipe(stylus({compress: true}))
        .pipe(imgurlsCachebust())
        .pipe(minifyCSS())
        .pipe(order(css_files,
                    {base: config.CSS_DEST_PATH}))
        .pipe(concat(paths.include_css))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('imgurls_write', ['css_build'], function() {
    // imgurls.txt is a list of cachebusted img URLs that is used by Zamboni
    // to generate the appcache manifest.
    return gulp.src(config.CSS_DEST_PATH + paths.include_css)
        .pipe(imgurlsAbsolutify())
        .pipe(imgurlsParse())
        .pipe(rename('imgurls.txt'))
        .pipe(gulp.dest('src/media'));
});

gulp.task('buildID_write', function() {
    // Writes build ID to src/media/build_id.txt.
    var buildID = new Date().getTime().toString();
    return gulpFile('build_id.txt', buildID)
        .pipe(gulp.dest('src/media'));
});

gulp.task('js_build', ['templates_build'], function() {
    // Uses the AMD optimizer to bundle our JS modules.
    // Will read our RequireJS config to handle shims, paths, and name
    // anonymous modules.
    // Traces all modules and outputs them in the correct order.
    return eventStream.merge(
        // Almond loader.
        gulp.src(paths.almond),
        // JS bundle.
        gulp.src(paths.js)
            .pipe(amdOptimize('main', {
                baseUrl: config.JS_DEST_PATH,
                findNestedDependencies: true,
                paths: config.requireConfig.paths,
                shim: config.requireConfig.shim,
                wrapShim: true
            }))
            .pipe(concat(paths.include_js)),
        // Init script.
        gulp.src(paths.init)
    )
        .pipe(order(['**/almond.js', '**/include.js', '**/init.js']))
        .pipe(uglify())
        .pipe(concat(paths.include_js))
        .pipe(gulp.dest(config.JS_DEST_PATH));
});

gulp.task('serve', ['css_compile', 'templates_build'], function() {
    // template -- template to serve (e.g., index (default), app, server).
    // port -- server port, defaults to config port or 8675.
    return gulp.src(['src'])
        .pipe(webserver({
            fallback: argv.template || 'index' + '.html',
            livereload: true,
            port: argv.port || config.PORT || 8675
        }));
});

gulp.task('clean', function() {
    return gulp.src([
        config.CSS_DEST_PATH + 'splash.css',
        config.CSS_DEST_PATH + paths.include_css,
        config.JS_DEST_PATH + paths.include_js,
        paths.styl_compiled,
        '_tmp',
        'src/locales',
        'src/media/locales',
        'src/media/build_id.txt',
        'src/media/imgurls.txt',
        'src/templates.js'
    ], {read: false})
        .pipe(clean({force: true}));
});

gulp.task('watch', function() {
    // Watch and recompile on change.
    // Note: does not detect new and deleted files while running.
    gulp.watch(paths.html, ['templates_build']);
    gulp.watch(paths.styl, ['css_compile']);
});

gulp.task('default', ['watch', 'serve']);
gulp.task('update', ['bower_copy', 'require_config']);
gulp.task('build', ['buildID_write', 'css_build', 'js_build',
                    'templates_build', 'imgurls_write']);

module.exports = {
    paths: paths
};
<|endoftext|>


<commit_before>"use strict";

// Check that the deps in node_modules match what's in package.json.
var safestart = require('safestart');
safestart(__dirname);

var fs = require('fs');
var path = require('path');

var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.
var request = require('request');

var launched_from_installer = false;

// Check if we need to kick off the python server-daemon (Desktop app)
if(fs.existsSync(__dirname + path.sep + "OpenBazaar-Server")) {
  launched_from_installer = true;

  console.log('Starting OpenBazaar Server');
  var platform = process.platform;

  if(platform == "darwin" || platform == "linux") {
    var subpy = require('child_process').spawn('/usr/local/bin/python', ['bootstrap.py', 'testnet', '--loglevel', 'debug'], {
      detach: true,
      cwd: __dirname + '/OpenBazaar-Server'
    });
    var stdout = '';
    var stderr = '';

    subpy.stdout.on('data', function (buf) {
      console.log('[STR] stdout "%s"', String(buf));
      stdout += buf;
    });
    subpy.stderr.on('data', function (buf) {
      console.log('[STR] stderr "%s"', String(buf));
      stderr += buf;
    });
    subpy.on('error', function(err) {
      console.log('Python error %s', String(err));
    });
    subpy.on('close', function (code) {
      console.log('exited with ' + code);
      console.log('[END] stdout "%s"', stdout);
      console.log('[END] stderr "%s"', stderr);
    });
    subpy.unref();
  }
}

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// You can use 'before-quit' instead of (or with) the close event
app.on('before-quit', function (e) {
    // Handle menu-item or keyboard shortcut quit here
    console.log('Closing Application');
    if(launched_from_installer) {
      console.log('Shutting down server daemon');
      request('http://localhost:18469/api/v1/shutdown', function (error, response, body) {
        if (!error && response.statusCode == 200) {
          console.log('Shutting down server');
        } else {
          console.log('Server does not seem to be running.');
        }
      });
    }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {

  // Create the browser window.
  mainWindow = new BrowserWindow({
    "width": 1200,
    "height": 720,
    "min-width": 1024,
    "min-height": 700,
    "center": true,
    "title": "OpenBazaar",
    "frame": false,
    "icon": "imgs/openbazaar-icon.png",
    "title-bar-style": "hidden"
  });

  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  // Open the devtools.
  mainWindow.openDevTools({detach: true});

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;

    if(subpy) {
      subpy.kill('SIGHUP');
    }
  });

  app.on('activate-with-no-open-windows', function() {
    mainWindow.show();
  });

});
<commit_msg>Change loadURL<commit_after>"use strict";

// Check that the deps in node_modules match what's in package.json.
var safestart = require('safestart');
safestart(__dirname);

var fs = require('fs');
var path = require('path');

var app = require('app');  // Module to control application life.
var BrowserWindow = require('electron').BrowserWindow;  // Module to create native browser window.
var request = require('request');

var launched_from_installer = false;

// Check if we need to kick off the python server-daemon (Desktop app)
if(fs.existsSync(__dirname + path.sep + "OpenBazaar-Server")) {
  launched_from_installer = true;

  console.log('Starting OpenBazaar Server');
  var platform = process.platform;

  if(platform == "darwin" || platform == "linux") {
    var subpy = require('child_process').spawn('/usr/local/bin/python', ['bootstrap.py', 'testnet', '--loglevel', 'debug'], {
      detach: true,
      cwd: __dirname + '/OpenBazaar-Server'
    });
    var stdout = '';
    var stderr = '';

    subpy.stdout.on('data', function (buf) {
      console.log('[STR] stdout "%s"', String(buf));
      stdout += buf;
    });
    subpy.stderr.on('data', function (buf) {
      console.log('[STR] stderr "%s"', String(buf));
      stderr += buf;
    });
    subpy.on('error', function(err) {
      console.log('Python error %s', String(err));
    });
    subpy.on('close', function (code) {
      console.log('exited with ' + code);
      console.log('[END] stdout "%s"', stdout);
      console.log('[END] stderr "%s"', stderr);
    });
    subpy.unref();
  }
}

// Report crashes to our server.
//require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// You can use 'before-quit' instead of (or with) the close event
app.on('before-quit', function (e) {
    // Handle menu-item or keyboard shortcut quit here
    console.log('Closing Application');
    if(launched_from_installer) {
      console.log('Shutting down server daemon');
      request('http://localhost:18469/api/v1/shutdown', function (error, response, body) {
        if (!error && response.statusCode == 200) {
          console.log('Shutting down server');
        } else {
          console.log('Server does not seem to be running.');
        }
      });
    }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {

  // Create the browser window.
  mainWindow = new BrowserWindow({
    "width": 1200,
    "height": 720,
    "min-width": 1024,
    "min-height": 700,
    "center": true,
    "title": "OpenBazaar",
    "frame": false,
    "icon": "imgs/openbazaar-icon.png",
    "title-bar-style": "hidden"
  });

  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  // Open the devtools.
  mainWindow.openDevTools({detach: true});

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;

    if(subpy) {
      subpy.kill('SIGHUP');
    }
  });

  app.on('activate-with-no-open-windows', function() {
    mainWindow.show();
  });

});
<|endoftext|>