<commit_before>const electron = require('electron');
const {app, BrowserWindow, dialog, Menu, MenuItem, Tray} = electron;
const AutoLaunch = require('auto-launch');
const notifier = require('node-notifier');
const path = require('path');
const request = require('request');
const storage = require('electron-json-storage');
const {ipcMain} = require('electron');
const child_process = require('child_process');
const {nativeImage} = require('electron');
const open = require('open');
const {setLocale} = require('./strings');
var strings = setLocale('en_US');

let win;
let tray = null;
let options = {};
let interval = null;
let status = null;
let isNew = false;

var launcher = new AutoLaunch({
	name: 'WoW Stat',
	path: process.platform === 'darwin' ? app.getAppPath() : process.execPath,
	isHidden: false
});

var createWindow = () => {
	win = new BrowserWindow({
		width: process.platform === 'darwin' ? 398 : 425,
		height: process.platform === 'darwin' ? 285 : 324,
		titleBarStyle: 'hidden',
		resizable: false,
		title: 'WoW Stat',
		show: false
	});

	win.loadURL(`file://${__dirname}/index.html`);

	win.on('closed', () => {
		win = null;
	});

	// dev
	if (process.env.ELECTRON_DEV) {
		win.webContents.openDevTools();
	}
};

var notify = (s, realm) => {
	console.log('notifying');
	var action = options[s ? 'actionUp' : 'actionDown'];
	if (action == 'notify') {
		let data = {
			body: 'The WoW realm ' + realm.name + ' is ' + (s ? 'back online' : 'offline'),
			title: realm.name + ' is ' + (s ? 'up' : 'down')
		};
		if (win) {
			win.webContents.send('notify', data);
		} else {
			exports.notify(data.title, data.message);
		}
		return;
	}

	if (action == 'launch' && options.path) {
		child_process.exec('open "' + options.path + '"', (error, stdout, stderr) => {
			if (error) {
				console.error(`exec error: ${error}`);
				return;
			}
			console.log(`stdout: ${stdout}`);
			console.log(`stderr: ${stderr}`);
		});
	}
};

var setStatus = (s) => {
	var icon = '';
	if (s.status == false) {
		icon = '-red';
	} else {
		if (s.queue == true) {
			icon = '-yellow';
		} else {
			icon = '-green';
		}
	}
	console.log('setting icon to '+icon);
	tray.setImage(nativeImage.createFromPath(path.join(__dirname) + '/w' + icon + '@2x.png'));
};

var checkServer = () => {
	request('http://cache.wowst.at/status?region=' + options.region, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			body = JSON.parse(body).hits.hits;
			var rs = [];
			for (var x in body) {
				rs.push(body[x]._source);
			}
			win.webContents.send('server-status', rs);
			exports.realms = rs;

			if (options.realm) {
				for (var x in rs) {

					if (rs[x].slug == options.realm) {

						if (rs[x].status != status) {
							setStatus(rs[x]);

							if (status === false || status === true) {
								if (rs[x].status) {
									notify(true, rs[x]);
								} else {
									notify(false, rs[x]);
								}
							}
						}

						status = rs[x].status;
						break;
					}
				}
			}
		}
	});
};

var changeInterval = () => {
	if (interval) {
		clearInterval(interval);
	}
	checkServer();
	interval = setInterval(() => {
		checkServer();
	}, options.intervalDown * 1000 * 60);
};

var createMenu = () => {
	return contextMenu = Menu.buildFromTemplate([
		{
			label: strings.settings,
			click: () => {
				if (win === null) {
					createWindow();
				} else {
					win.show();
				}
			}
		},
		{
			label: strings.enabled,
			type: 'checkbox',
			checked: true,
			click: (e) => {
				console.log(e.checked);
			}
		},
		{
			label: strings.launch,
			click: (e) => {
				open(options.path);
			}
		},
		{
			type: 'separator'
		},
		{
			label: strings.help,
			click: (e) => {
				open('http://wowst.at');
			}
		},
		{
			label: strings.quit,
			click: (e) => {
				app.quit();
			}
		}
	]);
};

app.on('ready', () => {

	if (process.platform === 'darwin') {

		const template = [
			{
				role: 'window',
				submenu: [
					{
						role: 'minimize'
					},
					{
						role: 'close'
					},
				]
			},
			{
				role: 'help',
				submenu: [
					{
						label: 'Learn More',
						click() {
							open('http://wowst.at');
						}
					},
				]
			},
		];

		// darwin specific
		template.unshift({
			label: 'WoW Stat',
			submenu: [
				{
					role: 'about'
				},
				{
					type: 'separator'
				},
				{
					role: 'services',
					submenu: []
				},
				{
					type: 'separator'
				},
				{
					role: 'hide'
				},
				{
					role: 'hideothers'
				},
				{
					role: 'unhide'
				},
				{
					type: 'separator'
				},
				{
					role: 'quit'
				},
			]
		});
		// Window menu.
		template[1].submenu = [
			{
				label: 'Close',
				accelerator: 'CmdOrCtrl+W',
				role: 'close'
			},
			{
				label: 'Minimize',
				accelerator: 'CmdOrCtrl+M',
				role: 'minimize'
			},
			{
				label: 'Zoom',
				role: 'zoom'
			},
			{
				type: 'separator'
			},
			{
				label: 'Bring All to Front',
				role: 'front'
			}
		];
		const menu = Menu.buildFromTemplate(template);
		Menu.setApplicationMenu(menu);
	};

	var image = nativeImage.createFromPath(path.join(__dirname) + '/w@2x.png');
	tray = new Tray(image);
	tray.setToolTip('WoW Stat')
	tray.setContextMenu(createMenu());

	options = {
		region: 'us',
		intervalUp: 5,
		intervalDown: 1,
		path: process.platform == 'darwin' ? '/Applications/World of Warcraft/World of Warcraft.app': 'C:\\Program Files\\World of Warcraft\\WoW.exe',
		actionUp: 'notify',
		actionDown: 'notify',
		autoload: false
	};

	changeInterval();

	storage.get('options', (error, o) => {
		if (error) throw error;
		console.log('current options', o, Object.keys(o).length);

		if (!Object.keys(o).length) {
			console.log('no options');
			isNew = true;
			storage.set('options', options, (error) => {
				if (error) throw error;
				console.log(options);
			});
		} else {
			console.log('options exist');
			options = o;
		}

		setTimeout(() => {
			if (isNew) {
				if (app.show) {
					app.show();
				}
				win.show();
			} else {
				if (app.hide) {
					app.hide();
				}
				win.hide();
			}
		},10);
	});

	createWindow();

	if (process.platform === 'darwin' && app.dock) {
		app.dock.hide();
	}
/*
	setTimeout(() => {
		storage.clear(() => {
		});
	},5000);
*/
});

app.on('window-all-closed', () => {
	//app.quit();
});

app.on('activate', () => {
	if (win === null) {
		createWindow();
	}
});

exports.selectDirectory = (fn) => {
	var res = dialog.showOpenDialog(win, {
		properties: ['openFile', 'openDirectory']
	});
	fn(res ? res[0] : null);
}

exports.setAutoload = (fn) => {
	launcher.enable();
}

exports.notify = (title, message, fn) => {
	notifier.notify({
		title: title,
		message: message,
		icon: path.join(__dirname, 'w@2x.png'),
		sound: true,
		contentImage: void 0,
		wait: false
	}, (err, response) => {
		console.log(arguments);
	});

	notifier.on('click', (notifierObject, options) => {
		fn();
	});
}

exports.hide = () => {
	win.hide();
}

exports.autoload = (on) => {
	if (options.autoload) {
		launcher.enable();
	} else {
		launcher.disable();
	}
};

exports.update = () => {
	changeInterval();
};

exports.options = (o) => {
	if (o) {
		options = o;
	}
	return options;
};

exports.realms = [];

exports.strings = strings;
exports.setLocale = (loc) => {
	strings = setLocale(loc);
	tray.setContextMenu(createMenu());
	return exports.strings = strings;
};

exports.isNew = isNew;<commit_msg>show app on tray click<commit_after>const electron = require('electron');
const {app, BrowserWindow, dialog, Menu, MenuItem, Tray} = electron;
const AutoLaunch = require('auto-launch');
const notifier = require('node-notifier');
const path = require('path');
const request = require('request');
const storage = require('electron-json-storage');
const {ipcMain} = require('electron');
const child_process = require('child_process');
const {nativeImage} = require('electron');
const open = require('open');
const {setLocale} = require('./strings');
var strings = setLocale('en_US');

let win;
let tray = null;
let options = {};
let interval = null;
let status = null;
let isNew = false;

var launcher = new AutoLaunch({
	name: 'WoW Stat',
	path: process.platform === 'darwin' ? app.getAppPath() : process.execPath,
	isHidden: false
});

var createWindow = () => {
	win = new BrowserWindow({
		width: process.platform === 'darwin' ? 398 : 425,
		height: process.platform === 'darwin' ? 285 : 324,
		titleBarStyle: 'hidden',
		resizable: false,
		title: 'WoW Stat',
		show: false
	});

	win.loadURL(`file://${__dirname}/index.html`);

	win.on('closed', () => {
		win = null;
	});

	// dev
	if (process.env.ELECTRON_DEV) {
		win.webContents.openDevTools();
	}
};

var notify = (s, realm) => {
	console.log('notifying');
	var action = options[s ? 'actionUp' : 'actionDown'];
	if (action == 'notify') {
		let data = {
			body: 'The WoW realm ' + realm.name + ' is ' + (s ? 'back online' : 'offline'),
			title: realm.name + ' is ' + (s ? 'up' : 'down')
		};
		if (win) {
			win.webContents.send('notify', data);
		} else {
			exports.notify(data.title, data.message);
		}
		return;
	}

	if (action == 'launch' && options.path) {
		child_process.exec('open "' + options.path + '"', (error, stdout, stderr) => {
			if (error) {
				console.error(`exec error: ${error}`);
				return;
			}
			console.log(`stdout: ${stdout}`);
			console.log(`stderr: ${stderr}`);
		});
	}
};

var setStatus = (s) => {
	var icon = '';
	if (s.status == false) {
		icon = '-red';
	} else {
		if (s.queue == true) {
			icon = '-yellow';
		} else {
			icon = '-green';
		}
	}
	console.log('setting icon to '+icon);
	tray.setImage(nativeImage.createFromPath(path.join(__dirname) + '/w' + icon + '@2x.png'));
};

var checkServer = () => {
	request('http://cache.wowst.at/status?region=' + options.region, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			body = JSON.parse(body).hits.hits;
			var rs = [];
			for (var x in body) {
				rs.push(body[x]._source);
			}
			win.webContents.send('server-status', rs);
			exports.realms = rs;

			if (options.realm) {
				for (var x in rs) {

					if (rs[x].slug == options.realm) {

						if (rs[x].status != status) {
							setStatus(rs[x]);

							if (status === false || status === true) {
								if (rs[x].status) {
									notify(true, rs[x]);
								} else {
									notify(false, rs[x]);
								}
							}
						}

						status = rs[x].status;
						break;
					}
				}
			}
		}
	});
};

var changeInterval = () => {
	if (interval) {
		clearInterval(interval);
	}
	checkServer();
	interval = setInterval(() => {
		checkServer();
	}, options.intervalDown * 1000 * 60);
};

var createMenu = () => {
	return contextMenu = Menu.buildFromTemplate([
		{
			label: strings.settings,
			click: () => {
				if (win === null) {
					createWindow();
				} else {
					win.show();
				}
			}
		},
		{
			label: strings.enabled,
			type: 'checkbox',
			checked: true,
			click: (e) => {
				console.log(e.checked);
			}
		},
		{
			label: strings.launch,
			click: (e) => {
				open(options.path);
			}
		},
		{
			type: 'separator'
		},
		{
			label: strings.help,
			click: (e) => {
				open('http://wowst.at');
			}
		},
		{
			label: strings.quit,
			click: (e) => {
				app.quit();
			}
		}
	]);
};

app.on('ready', () => {

	if (process.platform === 'darwin') {

		const template = [
			{
				role: 'window',
				submenu: [
					{
						role: 'minimize'
					},
					{
						role: 'close'
					},
				]
			},
			{
				role: 'help',
				submenu: [
					{
						label: 'Learn More',
						click() {
							open('http://wowst.at');
						}
					},
				]
			},
		];

		// darwin specific
		template.unshift({
			label: 'WoW Stat',
			submenu: [
				{
					role: 'about'
				},
				{
					type: 'separator'
				},
				{
					role: 'services',
					submenu: []
				},
				{
					type: 'separator'
				},
				{
					role: 'hide'
				},
				{
					role: 'hideothers'
				},
				{
					role: 'unhide'
				},
				{
					type: 'separator'
				},
				{
					role: 'quit'
				},
			]
		});
		// Window menu.
		template[1].submenu = [
			{
				label: 'Close',
				accelerator: 'CmdOrCtrl+W',
				role: 'close'
			},
			{
				label: 'Minimize',
				accelerator: 'CmdOrCtrl+M',
				role: 'minimize'
			},
			{
				label: 'Zoom',
				role: 'zoom'
			},
			{
				type: 'separator'
			},
			{
				label: 'Bring All to Front',
				role: 'front'
			}
		];
		const menu = Menu.buildFromTemplate(template);
		Menu.setApplicationMenu(menu);
	};

	var image = nativeImage.createFromPath(path.join(__dirname) + '/w@2x.png');
	tray = new Tray(image);
	tray.setToolTip('WoW Stat')
	tray.setContextMenu(createMenu());

	tray.on('clicked', function(event, bounds) {
		showWindow();
	});

	options = {
		region: 'us',
		intervalUp: 5,
		intervalDown: 1,
		path: process.platform == 'darwin' ? '/Applications/World of Warcraft/World of Warcraft.app': 'C:\\Program Files\\World of Warcraft\\WoW.exe',
		actionUp: 'notify',
		actionDown: 'notify',
		autoload: false
	};

	changeInterval();

	storage.get('options', (error, o) => {
		if (error) throw error;
		console.log('current options', o, Object.keys(o).length);

		if (!Object.keys(o).length) {
			console.log('no options');
			isNew = true;
			storage.set('options', options, (error) => {
				if (error) throw error;
				console.log(options);
			});
		} else {
			console.log('options exist');
			options = o;
		}

		setTimeout(() => {
			if (isNew) {
				if (app.show) {
					app.show();
				}
				win.show();
			} else {
				if (app.hide) {
					app.hide();
				}
				win.hide();
			}
		},10);
	});

	createWindow();

	if (process.platform === 'darwin' && app.dock) {
		app.dock.hide();
	}
/*
	setTimeout(() => {
		storage.clear(() => {
		});
	},5000);
*/
});

app.on('window-all-closed', () => {
	//app.quit();
});

app.on('activate', () => {
	if (win === null) {
		createWindow();
	}
});

exports.selectDirectory = (fn) => {
	var res = dialog.showOpenDialog(win, {
		properties: ['openFile', 'openDirectory']
	});
	fn(res ? res[0] : null);
}

exports.setAutoload = (fn) => {
	launcher.enable();
}

exports.notify = (title, message, fn) => {
	notifier.notify({
		title: title,
		message: message,
		icon: path.join(__dirname, 'w@2x.png'),
		sound: true,
		contentImage: void 0,
		wait: false
	}, (err, response) => {
		console.log(arguments);
	});

	notifier.on('click', (notifierObject, options) => {
		fn();
	});
}

exports.hide = () => {
	win.hide();
}

exports.autoload = (on) => {
	if (options.autoload) {
		launcher.enable();
	} else {
		launcher.disable();
	}
};

exports.update = () => {
	changeInterval();
};

exports.options = (o) => {
	if (o) {
		options = o;
	}
	return options;
};

exports.realms = [];

exports.strings = strings;
exports.setLocale = (loc) => {
	strings = setLocale(loc);
	tray.setContextMenu(createMenu());
	return exports.strings = strings;
};

exports.isNew = isNew;<|endoftext|>


<commit_before>var os = require( "os" );
var async = require( "async" );
var throng = require( "throng" );
var connections = require( "./shared/connections" );
var utils = require( "./shared/utils" );

var logger = connections.logger( [ "Pi-Sensor-RPC-Service" ] );

var run = function () {
    logger.log( "Starting Pi-Sensor-RPC-Service" );

    var serialPort = connections.serialport();
    var broker = connections.jackrabbit();

    var serialResponse = null;

    var serialWrite = function ( data ) {
        return function ( done ) {
            console.log( "Calling serial write" );
            serialResponse = null;
            serialPort.write( data, done );
        };
    };

    var serialDrain = function ( done ) {
        console.log( "Calling serial drain" );
        serialPort.drain( done );
    };

    var getSerialResponse = function ( done ) {
        console.log( "Calling serial response" );
        // var takingTooLong = false;
        // var start = new Date();
        // while ( !serialResponse && !takingTooLong ) {
        //     var end = new Date();
        //     takingTooLong = end - start > 2000;
        // }
        while ( !serialResponse ) {

        }

        return done( null, serialResponse );

        // if ( serialResponse ) {
        //     return done( null, serialResponse );
        // }
        // return done( new Error( "Serial response timeout" ) );
    };

    var handleMessage = function ( message, ack ) {
        console.log( "Calling message handler" );
        async.series( [
            serialWrite( message.serialMessage ),
            serialDrain,
            getSerialResponse
        ], function ( err, result ) {
            if ( err ) {
                console.log( err );
                logger.log( err );
                ack();
                process.exit( 1 );
            }
            return ack( result[ 2 ] );
        } );
    };

    var serve = function () {
        console.log( "Serve" );
        broker.handle( "sensor.get", handleMessage );
    };

    var create = function () {
        console.log( "Create" );
        broker.create( "sensor.get", { prefetch: 5 }, serve );
    };

    process.once( "uncaughtException", function ( err ) {
        logger.log( "Stopping Pi-Sensor-RPC-Service" );
        logger.log( err );
        process.exit();
    } );

    serialPort.on( "open", function () {
        console.log( "Serial Open" );
        serialPort.on( "data", function ( data ) {
            console.log( "Serial data handler registered" );
            serialResponse = utils.parseSerialData( data );
        } );
        logger.log( "Serial port open" );
        broker.once( "connected", create );
    } );
};

throng( run, {
    workers: os.cpus().length,
    lifetime: Infinity
} );
<commit_msg>I will figure this out<commit_after>var os = require( "os" );
var async = require( "async" );
var throng = require( "throng" );
var connections = require( "./shared/connections" );
var utils = require( "./shared/utils" );

var logger = connections.logger( [ "Pi-Sensor-RPC-Service" ] );

var run = function () {
    logger.log( "Starting Pi-Sensor-RPC-Service" );

    var serialPort = connections.serialport();
    var broker = connections.jackrabbit();

    var serialResponse = null;

    var serialWrite = function ( data ) {
        return function ( done ) {
            console.log( "Calling serial write with: " + data );
            serialResponse = null;
            serialPort.write( data, done );
        };
    };

    var serialDrain = function ( done ) {
        console.log( "Calling serial drain" );
        serialPort.drain( done );
    };

    var getSerialResponse = function ( done ) {
        console.log( "Calling serial response" );
        console.log( "serialResponse: " + serialResponse );
        return done( null, serialResponse );
        // var takingTooLong = false;
        // var start = new Date();
        // while ( !serialResponse && !takingTooLong ) {
        //     var end = new Date();
        //     takingTooLong = end - start > 2000;
        // }
        // while ( !serialResponse ) {

        // }

        // if ( serialResponse ) {
        //     return done( null, serialResponse );
        // }
        // return done( new Error( "Serial response timeout" ) );
    };

    var handleMessage = function ( message, ack ) {
        console.log( "Calling message handler - message.serialMessage: " + message.serialMessage );
        async.series( [
            serialWrite( message.serialMessage ),
            serialDrain,
            getSerialResponse
        ], function ( err, result ) {
            if ( err ) {
                console.log( err );
                logger.log( err );
                ack();
                process.exit( 1 );
            }
            console.log( result );
            return ack( result[ 2 ] );
        } );
    };

    var serve = function () {
        console.log( "Serve" );
        broker.handle( "sensor.get", handleMessage );
    };

    var create = function () {
        console.log( "Create" );
        broker.create( "sensor.get", { prefetch: 5 }, serve );
    };

    process.once( "uncaughtException", function ( err ) {
        logger.log( "Stopping Pi-Sensor-RPC-Service" );
        logger.log( err );
        process.exit();
    } );

    serialPort.on( "open", function () {
        console.log( "Serial Open" );
        serialPort.on( "data", function ( data ) {
            console.log( "Serial data handler called" );
            serialResponse = utils.parseSerialData( data );
        } );
        broker.once( "connected", create );
    } );
};

throng( run, {
    workers: os.cpus().length,
    lifetime: Infinity
} );
<|endoftext|>


<commit_before>import es6_promise from 'es6-promise'
import angular from 'angular'
import register from './utils/register'
import hrRes from 'angular-hy-res'
import formly from 'angular-formly'
import angularAnimate from 'angular-animate'
import 'ng-promise-status'
import './directives/progressBar/progressBar'

import applicationDirective from './directives/application/application'
import applicationHeaderDirective from './directives/applicationHeader/applicationHeader'
import pollsList from './directives/pollsList/pollsList'
import pollsService from './services/pollsService'
import pollDirective from './directives/poll/poll'
import sirenActionDirective from './directives/sirenAction/sirenAction'

es6_promise.polyfill();

angular.module('pollsClient', [
    'hrSiren',
    'hrLinkHeader',
    hrRes,
    formly,
    angularAnimate,
    'ngPromiseStatus',
    'isteven-omni-bar'
])
  .constant('apiLocation', 'http://polls.apiblueprint.org/')
  //.constant('apiLocation', 'http://private-851d-pollshypermedia.apiary-mock.com/')
  .config(['$compileProvider', function ($compileProvider) {
  // disable debug info
  $compileProvider.debugInfoEnabled(false);
}]);

register('pollsClient')
  .directive('application', applicationDirective)
  .directive('pollsList', pollsList)
  .directive('applicationHeader', applicationHeaderDirective)
  .directive('poll', pollDirective)
  .directive('sirenAction', sirenActionDirective)
  .service('pollsService', pollsService)
<commit_msg>Revert to https<commit_after>import es6_promise from 'es6-promise'
import angular from 'angular'
import register from './utils/register'
import hrRes from 'angular-hy-res'
import formly from 'angular-formly'
import angularAnimate from 'angular-animate'
import 'ng-promise-status'
import './directives/progressBar/progressBar'

import applicationDirective from './directives/application/application'
import applicationHeaderDirective from './directives/applicationHeader/applicationHeader'
import pollsList from './directives/pollsList/pollsList'
import pollsService from './services/pollsService'
import pollDirective from './directives/poll/poll'
import sirenActionDirective from './directives/sirenAction/sirenAction'

es6_promise.polyfill();

angular.module('pollsClient', [
    'hrSiren',
    'hrLinkHeader',
    hrRes,
    formly,
    angularAnimate,
    'ngPromiseStatus',
    'isteven-omni-bar'
])
  .constant('apiLocation', 'https://polls.apiblueprint.org/')
  //.constant('apiLocation', 'http://private-851d-pollshypermedia.apiary-mock.com/')
  .config(['$compileProvider', function ($compileProvider) {
  // disable debug info
  $compileProvider.debugInfoEnabled(false);
}]);

register('pollsClient')
  .directive('application', applicationDirective)
  .directive('pollsList', pollsList)
  .directive('applicationHeader', applicationHeaderDirective)
  .directive('poll', pollDirective)
  .directive('sirenAction', sirenActionDirective)
  .service('pollsService', pollsService)
<|endoftext|>


<commit_before>// node modules
var Path = nodeRequire('path');
var fs = nodeRequire('fs');
var os = nodeRequire('os');
var chokidar = nodeRequire('chokidar');

// front-end modules
var Vue = require('vue');
var $ = require('jquery');
var _ = require('underscore');
var keybindings = require('./keybindings');
var Files = require('./files');
var menu = require('./menu');
var windowstate = require('./windowstate');
var updater = require('./updater');
var settings = require('./settings');
var modes = {
  p5: require('./modes/p5/p5-mode')
};

var appConfig = {

  el: '#app',

  mode: modes.p5,

  components: {
    editor: require('./editor/index'),
    sidebar: require('./sidebar/index'),
    settings: require('./settings/index'),
    debug: require('./debug/index')
  },

  data: {
    title: 'Untitled',
    projectPath: window.PATH,
    unsaved: window.UNSAVED ? true : false,
    windowURL: window.location.href,
    temp: true,
    running: false,
    settings: {},
    showSettings: false,
    files: []
    //saveTarget: window
  },

  computed: {
    projectName: function() {
      return Path.basename(this.projectPath);
    }
  },

  ready: function() {
    this.modeFunction('update');
    updater.check();
    keybindings.setup(this);
    menu.setup(this);

    this.setupFileListener();
    this.setupCloseHandler();
    this.setupDragListener();
    this.setupSettings();

    if (this.projectPath) {
      if (!this.unsaved) this.temp = false;
      var filename = null;

      if (fs.lstatSync(this.projectPath).isFile()) {
        // keep the name of the file to be opened
        filename = this.projectPath;

        // set the projectPath to the enclosing folder
        this.projectPath = Path.dirname(this.projectPath);
      }

      // load the project and open the selected file
      var self = this;
      this.loadProject(this.projectPath, function(){
        if (filename) self.openFile(filename);
        gui.Window.get().show();
      });

      menu.updateRecentFiles(this, this.projectPath);
    } else {
      // if we don't have a project path global, create a new project
      this.modeFunction('newProject');
      menu.updateRecentFiles(this);
    }

  },

  methods: {
    modeFunction: function(func, args) {
      var mode = this.$options.mode;
      if (typeof mode[func] === 'function') {
        // make args an array if it isn't already
        // typeof args won't work because it returns 'object'
        if (Object.prototype.toString.call(args) !== '[object Array]') {
          args = [args];
        }
        mode[func].apply(this, args);
      }
    },

    setupSettings: function() {
      this.settings = settings.load();
      this.$watch('settings', function(value){
        this.$broadcast('settings-changed', value);
        settings.save(value);
      });
    },

    // use jquery to handle file changes
    // (I should move this over to vuejs but it wasn't dealing
    // with the html file element properly)
    setupFileListener: function() {
      $('#openFile').change(this.open.bind(this));
      $('#saveFile').change(this.saveAs.bind(this));
      $('#saveProject').change(this.saveProjectAs.bind(this));
    },

    setupCloseHandler: function() {
      var self = this;
      var win = gui.Window.get();
      win.on('close', function(closeEvent){
        // check to see if there are unsaved files
        var shouldClose = true;
        if (_.any(self.files, function(f) {return f.contents != f.originalContents})) {
          shouldClose = confirm('You have unsaved files. Quit and lose changes?');
        }
        if (shouldClose) {
          // clean up output window
          if (self.outputWindow) {
            self.outputWindow.close(true);
            self.outputWindow = null;
          }

          // save window state if the user quit the program
          if (closeEvent === 'quit') {
            windowstate.save(self, win);
          }

          // close this window
          this.close(true);
          win = null;
        }
      });
      win.on('focus', function(){
        menu.resetMenu();
      });
    },

    // todo: setup drag and drop
    setupDragListener: function() {
      var self = this;
      window.ondragover = function(e) { e.preventDefault(); return false };
      window.ondrop = function(e) {
        e.preventDefault();
        if (e.dataTransfer.files[0]) {
          var path = e.dataTransfer.files[0].path;
          self.openProject(path);
        }
        return false
      };
    },

    // create a new window 50px below current window
    newWindow: function(url, options) {
      var currentWindow = gui.Window.get();
      var win = gui.Window.open(url, _.extend({
        x: currentWindow.x + 50,
        y: currentWindow.y + 50,
        width: 1024,
        height: 700,
        toolbar: false,
        focus: true,
        show: false
      }, options));
      return win;
    },

    // open an existing project with a new window
    open: function(event) {
      var path = event.target.files[0].path;
      this.openProject(path);
      // reset value in case the user wants to open the same file more than once
      $('#openFile').val('');
    },

    openProject: function(path) {
      // create the new window
      var win = this.newWindow(this.windowURL);

      // set the project path of the new window
      win.on('document-start', function(){
        win.window.PATH = path;
      });

      return win;
    },

    // load project files
    loadProject: function(path, callback) {
      var self = this;
      Files.list(path, function(files){
        self.files = files;
        self.watch(path);
        if (typeof callback === 'function') callback();
      });
    },

    // watch the project file tree for changes
    watch: function(path) {
      var self = this;

      // don't watch recursively
      var watcher = chokidar.watch(path, {
        ignoreInitial: true,
        ignored: function(filepath) {
          var regex = new RegExp(path + '\/.*\/.+');
          return regex.test(filepath);
        }
      });

      watcher.on('add', function(path) {
        var f = Files.setup(path);
        Files.addToTree(f, self.files, self.projectPath);
      }).on('addDir', function(path) {
        var f = Files.setup(path, {type: 'folder', children: []});
        Files.addToTree(f, self.files, self.projectPath);
      }).on('unlink', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('unlinkDir', function(path) {
        Files.removeFromTree(path, self.files);
      });
    },

    // close the window, checking for unsaved file changes
    closeProject: function() {
      if (this.outputWindow) {
        this.outputWindow.close(true);
        this.outputWindow = null;
      }
      gui.Window.get().close();
    },

    // save all open files
    saveAll: function() {
      _.where(this.files, {type: 'file', open: true}).forEach(function(file) {
        if (file.originalContents != file.contents) {
          fs.writeFileSync(file.path, file.contents, "utf8");
          file.originalContents = file.contents;
        }
      });
      menu.updateRecentFiles(this, this.projectPath);
    },

    saveAs: function(event) {
      // capture the filename selected by the user
      var file = event.target.files[0].path;

      if (this.temp) {
        // mode specific action
        this.modeFunction('saveAs', file);
        menu.updateRecentFiles(this, this.projectPath);
      } else {
        // save a file
        // if the we are saving inside the project path just open the new file
        // otherwise open a new window
        fs.writeFileSync(file, this.currentFile.contents, "utf8");
        if ((Path.dirname(file) + '/').indexOf(this.projectPath + '/') > -1) {
          var f = Files.setup(file);
          Files.addToTree(f, this.files, this.projectPath);
          this.openFile(file);
        } else {
          this.openProject(file);
        }
      }

      // reset value in case the user wants to save the same filename more than once
      $('#saveFile').val('');
    },

    saveProjectAs: function(event) {
      var path = event.target.files[0].path;
      this.modeFunction('saveAs', path);
    },

    saveFile: function() {
      // if this is a new project then trigger a save-as
      if (this.temp) {
        $('#saveFile').trigger('click');
      } else {
        // otherwise just write the current file
        this.writeFile();
      }
    },

    writeFile: function() {
      fs.writeFileSync(this.currentFile.path, this.currentFile.contents, "utf8");
      this.currentFile.originalContents = this.currentFile.contents;
    },

    // open up a file - read its contents if it's not already opened
    openFile: function(path, callback) {
      var self = this;

      var file = Files.find(this.files, path);
      if (!file) return false;

      if (file.open) {
        this.title = file.name;
        this.currentFile = file;
        this.$broadcast('open-file', this.currentFile);
      } else {
        fs.readFile(path, 'utf8', function(err, fileContents) {
          if (err) throw err;
          file.contents = file.originalContents = fileContents;
          file.open = true;
          self.title = file.name;
          self.currentFile = file;
          self.$broadcast('open-file', self.currentFile);
          if (typeof callback === 'function') callback(file);
        });
      }
    },

    // create a new file and save it in the project path
    newFile: function(basepath) {
      var title = prompt('File name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.writeFile(filename, '', 'utf8', function(err){
        var f = Files.setup(filename);
        Files.addToTree(f, self.files, self.projectPath);
        self.openFile(filename);
      });
    },

    newFolder: function(basepath) {
      var title = prompt('Folder name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.mkdir(filename);
    },

    renameFile: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Rename ' + originalName + ' to:', originalName);
      if (!newName) return false;

      fs.rename(path, Path.join(Path.dirname(path), newName));
    },

    debugOut: function(msg, line, type) {
      if (typeof msg === 'object') msg = JSON.stringify(msg);
      $('#debug').append('
' + (line ? line + ': ' : '') + msg + '
');
      $('#debug').scrollTop($('#debug')[0].scrollHeight);
    },

    run: function() {
      $('#debug').html('');
      this.modeFunction('run');
    },

    toggleRun: function() {
      if (this.running) {
        this.modeFunction('stop');
      } else {
        $('#debug').html('');
        this.modeFunction('run');
      }
    },

    changeFontSize: function(sz) {
      this.settings.fontSize = parseInt(this.settings.fontSize) + sz;
    },

    toggleSettingsPane: function() {
      this.showSettings = !this.showSettings;
    },

    showHelp: function() {
      gui.Shell.openExternal(this.$options.mode.referenceURL);
    }

  }

};

windowstate.load(function(createNewProject){
  if (createNewProject) {
    var app = new Vue(appConfig);
  } else {
    gui.Window.get().close(true);
  }
});
<commit_msg>really fix window close bug<commit_after>// node modules
var Path = nodeRequire('path');
var fs = nodeRequire('fs');
var os = nodeRequire('os');
var chokidar = nodeRequire('chokidar');

// front-end modules
var Vue = require('vue');
var $ = require('jquery');
var _ = require('underscore');
var keybindings = require('./keybindings');
var Files = require('./files');
var menu = require('./menu');
var windowstate = require('./windowstate');
var updater = require('./updater');
var settings = require('./settings');
var modes = {
  p5: require('./modes/p5/p5-mode')
};

var appConfig = {

  el: '#app',

  mode: modes.p5,

  components: {
    editor: require('./editor/index'),
    sidebar: require('./sidebar/index'),
    settings: require('./settings/index'),
    debug: require('./debug/index')
  },

  data: {
    title: 'Untitled',
    projectPath: window.PATH,
    unsaved: window.UNSAVED ? true : false,
    windowURL: window.location.href,
    temp: true,
    running: false,
    focused: false,
    settings: {},
    showSettings: false,
    files: []
    //saveTarget: window
  },

  computed: {
    projectName: function() {
      return Path.basename(this.projectPath);
    }
  },

  ready: function() {
    this.modeFunction('update');
    updater.check();
    keybindings.setup(this);
    menu.setup(this);

    this.setupFileListener();
    this.setupCloseHandler();
    this.setupDragListener();
    this.setupSettings();

    if (this.projectPath) {
      if (!this.unsaved) this.temp = false;
      var filename = null;

      if (fs.lstatSync(this.projectPath).isFile()) {
        // keep the name of the file to be opened
        filename = this.projectPath;

        // set the projectPath to the enclosing folder
        this.projectPath = Path.dirname(this.projectPath);
      }

      // load the project and open the selected file
      var self = this;
      this.loadProject(this.projectPath, function(){
        if (filename) self.openFile(filename);
        gui.Window.get().show();
      });

      menu.updateRecentFiles(this, this.projectPath);
    } else {
      // if we don't have a project path global, create a new project
      this.modeFunction('newProject');
      menu.updateRecentFiles(this);
    }

  },

  methods: {
    modeFunction: function(func, args) {
      var mode = this.$options.mode;
      if (typeof mode[func] === 'function') {
        // make args an array if it isn't already
        // typeof args won't work because it returns 'object'
        if (Object.prototype.toString.call(args) !== '[object Array]') {
          args = [args];
        }
        mode[func].apply(this, args);
      }
    },

    setupSettings: function() {
      this.settings = settings.load();
      this.$watch('settings', function(value){
        this.$broadcast('settings-changed', value);
        settings.save(value);
      });
    },

    // use jquery to handle file changes
    // (I should move this over to vuejs but it wasn't dealing
    // with the html file element properly)
    setupFileListener: function() {
      $('#openFile').change(this.open.bind(this));
      $('#saveFile').change(this.saveAs.bind(this));
      $('#saveProject').change(this.saveProjectAs.bind(this));
    },

    setupCloseHandler: function() {
      var self = this;
      var win = gui.Window.get();
      win.on('close', function(closeEvent){
        // check to see if there are unsaved files
        var shouldClose = true;
        if (_.any(self.files, function(f) {return f.contents != f.originalContents})) {
          shouldClose = confirm('You have unsaved files. Quit and lose changes?');
        }
        if (shouldClose) {
          // clean up output window
          if (self.outputWindow) {
            self.outputWindow.close(true);
            self.outputWindow = null;
          }

          // save window state if the user quit the program
          if (closeEvent === 'quit') {
            windowstate.save(self, win);
          }

          // close this window
          this.close(true);
          win = null;
        }
      });

      win.on('focus', function(){
        self.focused = true;
        menu.resetMenu();
      });

      win.on('blur', function(){
        self.focused = false;
      });
    },

    // todo: setup drag and drop
    setupDragListener: function() {
      var self = this;
      window.ondragover = function(e) { e.preventDefault(); return false };
      window.ondrop = function(e) {
        e.preventDefault();
        if (e.dataTransfer.files[0]) {
          var path = e.dataTransfer.files[0].path;
          self.openProject(path);
        }
        return false
      };
    },

    // create a new window 50px below current window
    newWindow: function(url, options) {
      var currentWindow = gui.Window.get();
      var win = gui.Window.open(url, _.extend({
        x: currentWindow.x + 50,
        y: currentWindow.y + 50,
        width: 1024,
        height: 700,
        toolbar: false,
        focus: true,
        show: false
      }, options));
      return win;
    },

    // open an existing project with a new window
    open: function(event) {
      var path = event.target.files[0].path;
      this.openProject(path);
      // reset value in case the user wants to open the same file more than once
      $('#openFile').val('');
    },

    openProject: function(path) {
      // create the new window
      var win = this.newWindow(this.windowURL);

      // set the project path of the new window
      win.on('document-start', function(){
        win.window.PATH = path;
      });

      return win;
    },

    // load project files
    loadProject: function(path, callback) {
      var self = this;
      Files.list(path, function(files){
        self.files = files;
        self.watch(path);
        if (typeof callback === 'function') callback();
      });
    },

    // watch the project file tree for changes
    watch: function(path) {
      var self = this;

      // don't watch recursively
      var watcher = chokidar.watch(path, {
        ignoreInitial: true,
        ignored: function(filepath) {
          var regex = new RegExp(path + '\/.*\/.+');
          return regex.test(filepath);
        }
      });

      watcher.on('add', function(path) {
        var f = Files.setup(path);
        Files.addToTree(f, self.files, self.projectPath);
      }).on('addDir', function(path) {
        var f = Files.setup(path, {type: 'folder', children: []});
        Files.addToTree(f, self.files, self.projectPath);
      }).on('unlink', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('unlinkDir', function(path) {
        Files.removeFromTree(path, self.files);
      });
    },

    // close the window, checking for unsaved file changes
    closeProject: function() {
      if (this.focused) {
        if (this.outputWindow) {
          this.outputWindow.close(true);
          this.outputWindow = null;
        }
        gui.Window.get().close();
      } else {
        if (this.outputWindow) {
          this.outputWindow.close(true);
          this.outputWindow = null;
        }
      }
    },

    // save all open files
    saveAll: function() {
      _.where(this.files, {type: 'file', open: true}).forEach(function(file) {
        if (file.originalContents != file.contents) {
          fs.writeFileSync(file.path, file.contents, "utf8");
          file.originalContents = file.contents;
        }
      });
      menu.updateRecentFiles(this, this.projectPath);
    },

    saveAs: function(event) {
      // capture the filename selected by the user
      var file = event.target.files[0].path;

      if (this.temp) {
        // mode specific action
        this.modeFunction('saveAs', file);
        menu.updateRecentFiles(this, this.projectPath);
      } else {
        // save a file
        // if the we are saving inside the project path just open the new file
        // otherwise open a new window
        fs.writeFileSync(file, this.currentFile.contents, "utf8");
        if ((Path.dirname(file) + '/').indexOf(this.projectPath + '/') > -1) {
          var f = Files.setup(file);
          Files.addToTree(f, this.files, this.projectPath);
          this.openFile(file);
        } else {
          this.openProject(file);
        }
      }

      // reset value in case the user wants to save the same filename more than once
      $('#saveFile').val('');
    },

    saveProjectAs: function(event) {
      var path = event.target.files[0].path;
      this.modeFunction('saveAs', path);
    },

    saveFile: function() {
      // if this is a new project then trigger a save-as
      if (this.temp) {
        $('#saveFile').trigger('click');
      } else {
        // otherwise just write the current file
        this.writeFile();
      }
    },

    writeFile: function() {
      fs.writeFileSync(this.currentFile.path, this.currentFile.contents, "utf8");
      this.currentFile.originalContents = this.currentFile.contents;
    },

    // open up a file - read its contents if it's not already opened
    openFile: function(path, callback) {
      var self = this;

      var file = Files.find(this.files, path);
      if (!file) return false;

      if (file.open) {
        this.title = file.name;
        this.currentFile = file;
        this.$broadcast('open-file', this.currentFile);
      } else {
        fs.readFile(path, 'utf8', function(err, fileContents) {
          if (err) throw err;
          file.contents = file.originalContents = fileContents;
          file.open = true;
          self.title = file.name;
          self.currentFile = file;
          self.$broadcast('open-file', self.currentFile);
          if (typeof callback === 'function') callback(file);
        });
      }
    },

    // create a new file and save it in the project path
    newFile: function(basepath) {
      var title = prompt('File name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.writeFile(filename, '', 'utf8', function(err){
        var f = Files.setup(filename);
        Files.addToTree(f, self.files, self.projectPath);
        self.openFile(filename);
      });
    },

    newFolder: function(basepath) {
      var title = prompt('Folder name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.mkdir(filename);
    },

    renameFile: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Rename ' + originalName + ' to:', originalName);
      if (!newName) return false;

      fs.rename(path, Path.join(Path.dirname(path), newName));
    },

    debugOut: function(msg, line, type) {
      if (typeof msg === 'object') msg = JSON.stringify(msg);
      $('#debug').append('
' + (line ? line + ': ' : '') + msg + '
');
      $('#debug').scrollTop($('#debug')[0].scrollHeight);
    },

    run: function() {
      $('#debug').html('');
      this.modeFunction('run');
    },

    toggleRun: function() {
      if (this.running) {
        this.modeFunction('stop');
      } else {
        $('#debug').html('');
        this.modeFunction('run');
      }
    },

    changeFontSize: function(sz) {
      this.settings.fontSize = parseInt(this.settings.fontSize) + sz;
    },

    toggleSettingsPane: function() {
      this.showSettings = !this.showSettings;
    },

    showHelp: function() {
      gui.Shell.openExternal(this.$options.mode.referenceURL);
    }

  }

};

windowstate.load(function(createNewProject){
  if (createNewProject) {
    var app = new Vue(appConfig);
  } else {
    gui.Window.get().close(true);
  }
});
<|endoftext|>


<commit_before>import deepFreeze from 'deep-freeze';
import expect from 'expect';
import { createStore, combineReducers } from 'redux';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';

const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        id: action.id,
        text: action.text,
        completed:false
      };
    case 'TOGGLE_TODO': {
      if (state.id !== action.id) {
        return state;
      };

      return {
        ...state,
        completed: !state.completed
      }
    }
    default:
      return state
  }
};

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        todo(undefined, action)
      ];
    case 'TOGGLE_TODO':
      return state.map(t => todo(t, action));
    default:
      return state;
  }
};

const visibilityFilter = (
  state = 'SHOW_ALL',
  action
) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state;
  }
};

let nextTodoId = 0;
const addTodo = (text) => {
  return {
    type: 'ADD_TODO',
    id: nextTodoId++,
    text: text
  };
}

const setVisibilityFilter = (filter) => {
  return {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

const todoApp = combineReducers({
  todos,//: todos,
  visibilityFilter,//: visibilityFilter <-- don't need to specify if same name (thanks to es6 object literal shorthand notation)
});

/* Presentation Component */
const Link = ({
  active,
  children,
  onClick
}) => {
  if (active) { return {children}}
  return (
     {
        e.preventDefault();
        onClick();
      }}
    >
    {children}

  );
}

const mapStateToLinkProps = (
  state,
  ownProps
) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  };
};

const mapDispatchToLinkProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch(setVisibilityFilter(ownProps.filter))
    }
  };
};

const FilterLink = connect(
  mapStateToLinkProps,
  mapDispatchToLinkProps
)(Link);

/* Presentation Component */
const Footer = () => (

    Show:
    {' '}
    <FilterLink filter='SHOW_ALL'>All</FilterLink>
    {' '}
    <FilterLink filter='SHOW_ACTIVE'>Active</FilterLink>
    {' '}
    <FilterLink filter='SHOW_COMPLETED'>Completed</FilterLink>

);

const Todo = ({
  onClick,
  completed,
  text
}) => (

    {text}

);

/* Presentation Component */
const TodoList = ({
  todos,
  onTodoClick
}) => (

    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}

);

/*
  Not presentation nor container, doesn't fit either
   - input adn button are presentational
   - dispatching action is container

  Keeping together because they don't need to be seperated
*/

let AddTodo = ({ dispatch }) => {
  let input;

  return(

      <input ref={node => {
          input = node
      }} />
      <button onClick={() => {
        dispatch(addTodo(input.value));
        input.value = '';
      }}>
        Add Todo
      </button>

  );
}
AddTodo = connect(
  /*
  This is what it looks like with default param

    state => {
      return {};
    },
    dispatch => {
      return { dispatch };
    }

  Which could just be this
    null, // don't need to actually subscribe to store
    null // <-- get dispatch for free

  Therefore, if both null we can just leave empty
  */
)(AddTodo);

const getVisibleTodos = (
  todos,
  filter
) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed);
    default:
      return todos;
  }
}

// Maps the redux store state to the
// Props of the TodoList component
const mapStateToTodoListProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  };
};

// Maps the dispatch method of the store
// to the callback props of the TodoList component
const mapDispatchToTodoListProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch({
        type: 'TOGGLE_TODO',
        id
      })
    }
  }
}

const VisibleTodoList = connect(
  mapStateToTodoListProps,
  mapDispatchToTodoListProps
)(TodoList)

const TodoApp = () => (

    <AddTodo />
    <VisibleTodoList />

);

// if you don't want redux dev tools, just use this:
// const store = createStore(todoApp);
const finalStore = createStore(
  todoApp,
  {}, // initial state
  window.devToolsExtension ? window.devToolsExtension() : undefined
);

ReactDOM.render(
  <Provider store={finalStore} >
    <TodoApp />
  </Provider>,
  document.getElementById('root')
);
<commit_msg>final episode<commit_after>import deepFreeze from 'deep-freeze';
import expect from 'expect';
import { createStore, combineReducers } from 'redux';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';

const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        id: action.id,
        text: action.text,
        completed:false
      };
    case 'TOGGLE_TODO': {
      if (state.id !== action.id) {
        return state;
      };

      return {
        ...state,
        completed: !state.completed
      }
    }
    default:
      return state
  }
};

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        todo(undefined, action)
      ];
    case 'TOGGLE_TODO':
      return state.map(t => todo(t, action));
    default:
      return state;
  }
};

const visibilityFilter = (
  state = 'SHOW_ALL',
  action
) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state;
  }
};

let nextTodoId = 0;
const addTodo = (text) => {
  return {
    type: 'ADD_TODO',
    id: nextTodoId++,
    text
  };
}

const setVisibilityFilter = (filter) => {
  return {
    type: 'SET_VISIBILITY_FILTER',
    filter
  };
}

const toggleTodo = (id) => {
  return {
    type: 'TOGGLE_TODO',
    id
  };
}

const todoApp = combineReducers({
  todos,//: todos,
  visibilityFilter,//: visibilityFilter <-- don't need to specify if same name (thanks to es6 object literal shorthand notation)
});

/* Presentation Component */
const Link = ({
  active,
  children,
  onClick
}) => {
  if (active) { return {children}}
  return (
     {
        e.preventDefault();
        onClick();
      }}
    >
    {children}

  );
}

const mapStateToLinkProps = (
  state,
  ownProps
) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  };
};

const mapDispatchToLinkProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch(setVisibilityFilter(ownProps.filter))
    }
  };
};

const FilterLink = connect(
  mapStateToLinkProps,
  mapDispatchToLinkProps
)(Link);

/* Presentation Component */
const Footer = () => (

    Show:
    {' '}
    <FilterLink filter='SHOW_ALL'>All</FilterLink>
    {' '}
    <FilterLink filter='SHOW_ACTIVE'>Active</FilterLink>
    {' '}
    <FilterLink filter='SHOW_COMPLETED'>Completed</FilterLink>

);

const Todo = ({
  onClick,
  completed,
  text
}) => (

    {text}

);

/* Presentation Component */
const TodoList = ({
  todos,
  onTodoClick
}) => (

    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}

);

/*
  Not presentation nor container, doesn't fit either
   - input adn button are presentational
   - dispatching action is container

  Keeping together because they don't need to be seperated
*/

let AddTodo = ({ dispatch }) => {
  let input;

  return(

      <input ref={node => {
          input = node
      }} />
      <button onClick={() => {
        dispatch(addTodo(input.value));
        input.value = '';
      }}>
        Add Todo
      </button>

  );
}
AddTodo = connect(
  /*
  This is what it looks like with default param

    state => {
      return {};
    },
    dispatch => {
      return { dispatch };
    }

  Which could just be this
    null, // don't need to actually subscribe to store
    null // <-- get dispatch for free

  Therefore, if both null we can just leave empty
  */
)(AddTodo);

const getVisibleTodos = (
  todos,
  filter
) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed);
    default:
      return todos;
  }
}

// Maps the redux store state to the
// Props of the TodoList component
const mapStateToTodoListProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  };
};

// Maps the dispatch method of the store
// to the callback props of the TodoList component
const mapDispatchToTodoListProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToTodoListProps,
  mapDispatchToTodoListProps
)(TodoList)

const TodoApp = () => (

    <AddTodo />
    <VisibleTodoList />

);

// if you don't want redux dev tools, just use this:
// const store = createStore(todoApp);
const finalStore = createStore(
  todoApp,
  {}, // initial state
  window.devToolsExtension ? window.devToolsExtension() : undefined
);

ReactDOM.render(
  <Provider store={finalStore} >
    <TodoApp />
  </Provider>,
  document.getElementById('root')
);
<|endoftext|>


<commit_before>import React from 'react';
import Router from 'react-router';
import routes from './routes';

Router.run(routes, Router.HistoryLocation, function(Handler) {
  React.render(<Handler />, document);
});<commit_msg>render app into #app container<commit_after>import React from 'react';
import Router from 'react-router';
import routes from './routes';

Router.run(routes, Router.HistoryLocation, function(Handler) {
  React.render(<Handler />, document.getElementById('app'));
});<|endoftext|>


<commit_before>var app = require('app');
var path = require('path');
var BrowserWindow = require('browser-window');
var Shell = require('shell');
var ConfigStore = require('configstore');
var Menu = require('./menu');
var SquirrelWindows = require('./squirrel_windows');

if (SquirrelWindows.handleStartupEvent()) {
  return;
}

var mainWindow = null;
var menu = null;
const host = 'https://www.irccloud.com';
const config = new ConfigStore(app.getName(), {
  'width': 1024,
  'height': 768
});

var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
  // Someone tried to run a second instance, we should focus our window
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
        mainWindow.restore();
    }
    mainWindow.focus();
  }
  return true;
});

if (shouldQuit) {
  app.quit();
  return;
}

function openMainWindow() {
  mainWindow = new BrowserWindow({
    'width': config.get('width'),
    'height': config.get('height'),
    'webPreferences': {
      'allowDisplayingInsecureContent': true,
      'preload': path.join(__dirname, 'preload.js'),
      'nodeIntegration': false
    },
    'title': app.getName()
  });
  mainWindow.loadURL(host);

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

  mainWindow.on('resize', function() {
    size = mainWindow.getSize();
    config.set({
      'width': size[0],
      'height': size[1]
    });
  });

  mainWindow.on('page-title-updated', function(event) {
      var title = mainWindow.getTitle();
      if (title && process.platform == 'darwin') {
          var unread = "";
          var matches = title.match(/^\((\d+)\)/);
          if (matches) {
            unread = matches[1];
          }
          app.dock.setBadge(unread);
      }
  });

  mainWindow.on('app-command', function (e, cmd) {
    switch (cmd) {
    case 'browser-backward':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    case 'browser-forward':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    default:
      break;
    }
  });

  mainWindow.webContents.on('did-navigate-in-page', function (e, url) {
    var historyMenu = menu.items.find(function (item) {
      return item.id == 'history';
    });
    historyMenu.submenu.items.forEach(function (historyItem) {
      switch (historyItem.id) {
      case 'backMenu':
        historyItem.enabled = mainWindow.webContents.canGoBack();
        break;
      case 'fwdMenu':
        historyItem.enabled = mainWindow.webContents.canGoForward();
        break;
      default:
        break;
      }
    });
  });

  mainWindow.webContents.on('new-window', function(event, url, frameName, disposition) {
      event.preventDefault();
      Shell.openExternal(url);
  });
}

app.on('activate-with-no-open-windows', openMainWindow);
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {
  menu = Menu.setup(app, host);
  openMainWindow();
});
<commit_msg>Support swipe-left/right when they land upstream<commit_after>var app = require('app');
var path = require('path');
var BrowserWindow = require('browser-window');
var Shell = require('shell');
var ConfigStore = require('configstore');
var Menu = require('./menu');
var SquirrelWindows = require('./squirrel_windows');

if (SquirrelWindows.handleStartupEvent()) {
  return;
}

var mainWindow = null;
var menu = null;
const host = 'https://www.irccloud.com';
const config = new ConfigStore(app.getName(), {
  'width': 1024,
  'height': 768
});

var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
  // Someone tried to run a second instance, we should focus our window
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
        mainWindow.restore();
    }
    mainWindow.focus();
  }
  return true;
});

if (shouldQuit) {
  app.quit();
  return;
}

function openMainWindow() {
  mainWindow = new BrowserWindow({
    'width': config.get('width'),
    'height': config.get('height'),
    'webPreferences': {
      'allowDisplayingInsecureContent': true,
      'preload': path.join(__dirname, 'preload.js'),
      'nodeIntegration': false
    },
    'title': app.getName()
  });
  mainWindow.loadURL(host);

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

  mainWindow.on('swipe-right', function (e) {
    if (mainWindow.webContents.canGoForward()) {
      mainWindow.webContents.goForward();
    }
  });

  mainWindow.on('swipe-left', function (e) {
    if (mainWindow.webContents.canGoBack()) {
      mainWindow.webContents.goBack();
    }
  });

  mainWindow.on('resize', function() {
    size = mainWindow.getSize();
    config.set({
      'width': size[0],
      'height': size[1]
    });
  });

  mainWindow.on('page-title-updated', function(event) {
      var title = mainWindow.getTitle();
      if (title && process.platform == 'darwin') {
          var unread = "";
          var matches = title.match(/^\((\d+)\)/);
          if (matches) {
            unread = matches[1];
          }
          app.dock.setBadge(unread);
      }
  });

  mainWindow.on('app-command', function (e, cmd) {
    switch (cmd) {
    case 'browser-backward':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    case 'browser-forward':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    default:
      break;
    }
  });

  mainWindow.webContents.on('did-navigate-in-page', function (e, url) {
    var historyMenu = menu.items.find(function (item) {
      return item.id == 'history';
    });
    historyMenu.submenu.items.forEach(function (historyItem) {
      switch (historyItem.id) {
      case 'backMenu':
        historyItem.enabled = mainWindow.webContents.canGoBack();
        break;
      case 'fwdMenu':
        historyItem.enabled = mainWindow.webContents.canGoForward();
        break;
      default:
        break;
      }
    });
  });

  mainWindow.webContents.on('new-window', function(event, url, frameName, disposition) {
      event.preventDefault();
      Shell.openExternal(url);
  });
}

app.on('activate-with-no-open-windows', openMainWindow);
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {
  menu = Menu.setup(app, host);
  openMainWindow();
});
<|endoftext|>


<commit_before>var app = require('app');
var ipc = require('ipc');
var fs = require('fs');
var BrowserWindow = require('browser-window');
var defaults = require('./src/defaults');

var settings = null;
var settingsFilePath = null;

var core = null;
var mainWindow = null;

app.on('ready', function() {
  settingsFilePath = app.getPath('userData') + '/settings.json';
  loadSettings();

  core = new BrowserWindow({
    show: false
  });
  core.loadUrl('file://' + __dirname + '/core.html');

  mainWindow = new BrowserWindow({
    width: settings.windowWidth,
    height: settings.windowHeight,
    'min-width': 650,
    'min-height': 374,
    center: true
  });
  mainWindow.loadUrl('file://' + __dirname + '/loading.html');

  ipc.on('core-server-ready', function() {
    mainWindow.loadUrl('http://localhost:' + settings.port);
    mainWindow.toggleDevTools();
  });

  core.toggleDevTools();

  mainWindow.on('close', function() {
    saveSettings();
  });

  mainWindow.on('closed', function() {
    if (core && core !== null) {
      core.close();
    }

    mainWindow = null;
  });

  core.on('closed', function() {
    core = null;
  });
});

function loadSettings() {
  try {
    settings = JSON.parse(fs.readFileSync(settingsFilePath));
  } catch (err) {
    // If file doesn't exist - create default settings
    settings = {
      windowWidth: defaults.windowWidth,
      windowHeight: defaults.windowHeight,
      port: defaults.port
    };

    saveSettings();
  }
}

function saveSettings() {
  fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2));
}

ipc.on('settings-get', function(event) {
  event.returnValue = settings;
});

ipc.on('settings-change', function(event, arg) {
  settings = arg;
  event.returnValue = null;
});

ipc.on('settings-save', function(event) {
  saveSettings();
  event.returnValue = null;
});
<commit_msg>Code comments for main process<commit_after>var app = require('app');
var ipc = require('ipc');
var globalShortcut = require('global-shortcut');
var fs = require('fs');
var BrowserWindow = require('browser-window');
var defaults = require('./src/defaults');

var settings = null;
var settingsFilePath = null;

var core = null;
var mainWindow = null;

app.on('ready', function() {

  // Load settings from file
  settingsFilePath = app.getPath('userData') + '/settings.json';
  loadSettings();

  // Create 'core' window (player)
  core = new BrowserWindow({
    show: false
  });
  core.loadUrl('file://' + __dirname + '/core.html');

  // Create 'main' window (user interface)
  mainWindow = new BrowserWindow({
    width: settings.windowWidth,
    height: settings.windowHeight,
    'min-width': 650,
    'min-height': 374,
    center: true
  });

  // Display loading screen
  mainWindow.loadUrl('file://' + __dirname + '/loading.html');

  // When Core is ready load proper UI
  ipc.on('core-server-ready', function() {
    mainWindow.loadUrl('http://localhost:' + settings.port);
    mainWindow.toggleDevTools();
  });

  // Open devtools for Core window
  core.toggleDevTools();

  // Save settings before main window closes
  mainWindow.on('close', function() {
    saveSettings();
  });

  // When main window is closed, close Core window
  mainWindow.on('closed', function() {
    if (core && core !== null) {
      core.close();
    }

    mainWindow = null;
  });

  // Kill process
  core.on('closed', function() {
    core = null;
  });
});

// Load settings from file
function loadSettings() {
  try {
    settings = JSON.parse(fs.readFileSync(settingsFilePath));
  } catch (err) {
    // If file doesn't exist - create default settings
    settings = {
      windowWidth: defaults.windowWidth,
      windowHeight: defaults.windowHeight,
      port: defaults.port
    };

    saveSettings();
  }
}

// Save settings to file
function saveSettings() {
  fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2));
}

ipc.on('settings-get', function(event) {
  event.returnValue = settings;
});

ipc.on('settings-change', function(event, arg) {
  settings = arg;
  event.returnValue = null;
});

ipc.on('settings-save', function(event) {
  saveSettings();
  event.returnValue = null;
});
<|endoftext|>


<commit_before>require(['jquery', 'dyole/graph', 'dyole/constants/NodeModel', 'lodash'], function ($, Graph, NodeModel, _) {

    var canvas = new Graph({
        $parent: $('.graph-placeholder'),
        editMode: true
    });

    var constraints = {
        labelOffset: 40,
        radius:60
    };

    var n1, n2, n3, n4, n5, n6 , n7;

    n1 = canvas.addNode(NodeModel.get(), {x: 200, y: 200}, false, constraints);
    n2 = canvas.addNode(NodeModel.get(), {x: 450, y: 100}, false);
    n3 = canvas.addNode(NodeModel.get(), {x: 600, y: 200}, false);
    n4 = canvas.addNode(NodeModel.get(), {x: 800, y: 200}, false);
    n5 = canvas.addNode(NodeModel.get(), {x: 300, y: 300}, false);
    n6 = canvas.addNode(NodeModel.get(), {x: 500, y: 300}, false);
    n7 = canvas.addNode(NodeModel.get(), {x: 700, y: 300}, false);

    canvas.connectNodes(n1, n2, 'labelName');
    canvas.connectNodes(n2, n3);
    canvas.connectNodes(n3, n4);
    canvas.connectNodes(n5, n2);
    canvas.connectNodes(n6, n3);
    canvas.connectNodes(n7, n4);

    canvas.Event.subscribe('node:showInfo', function (node) {

        console.log('info', node);

    });
    canvas.Event.subscribe('node:select', function (node) {

        console.log('select', node);
        var allNodeConnections =  canvas.getNodeById(node.id).connections;

        _.each(allNodeConnections, function(connectionObject) {
            connectionObject._glow = connectionObject.connection.getPathOuter().glow();
        });

    });

    canvas.Event.subscribe('node:deselected', function (node) {

        var allNodeConnections =  canvas.getNodeById(node.id).connections;

        _.each(allNodeConnections, function(connectionObject) {
            if (connectionObject._glow) {

                connectionObject._glow.remove();
            }
        });

    });

});<commit_msg>better clog<commit_after>require(['jquery', 'dyole/graph', 'dyole/constants/NodeModel', 'lodash'], function ($, Graph, NodeModel, _) {

    var canvas = new Graph({
        $parent: $('.graph-placeholder'),
        editMode: true
    });

    var constraints = {
        labelOffset: 40,
        radius:60
    };

    var n1, n2, n3, n4, n5, n6 , n7;

    n1 = canvas.addNode(NodeModel.get(), {x: 200, y: 200}, false, constraints);
    n2 = canvas.addNode(NodeModel.get(), {x: 450, y: 100}, false);
    n3 = canvas.addNode(NodeModel.get(), {x: 600, y: 200}, false);
    n4 = canvas.addNode(NodeModel.get(), {x: 800, y: 200}, false);
    n5 = canvas.addNode(NodeModel.get(), {x: 300, y: 300}, false);
    n6 = canvas.addNode(NodeModel.get(), {x: 500, y: 300}, false);
    n7 = canvas.addNode(NodeModel.get(), {x: 700, y: 300}, false);

    canvas.connectNodes(n1, n2, 'labelName');
    canvas.connectNodes(n2, n3);
    canvas.connectNodes(n3, n4);
    canvas.connectNodes(n5, n2);
    canvas.connectNodes(n6, n3);
    canvas.connectNodes(n7, n4);

    canvas.Event.subscribe('node:showInfo', function (node) {

        console.log('%cnode:showInfo', 'color:#acacff;background:black;', node);

    });

    canvas.Event.subscribe('node:select', function (node) {

        console.log('%cnode:select', 'color:#acacff;background:black;', node);

    });

    canvas.Event.subscribe('node:deselected', function (node) {

        console.log('%cnode:deselected', 'color:#acacff;background:black;', node);

    });

});<|endoftext|>


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

// 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': 800,
    'height': 600,
    'min-width': 400,
    'min-height': 400,
    'auto-hide-menu-bar': true
  });

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

  // Open the devtools.
  //mainWindow.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // De-reference 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;
  });
});
<commit_msg>clean up code<commit_after>var app = require('app');
var BrowserWindow = require('browser-window');
var mainWindow = null;

/**
 * quit when all window are closed
 **/
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

/**
 * when electron finished initialization
 **/
app.on('ready', function() {
  mainWindow = new BrowserWindow({
    'width': 800,
    'height': 600,
    'min-width': 400,
    'min-height': 400,
    'auto-hide-menu-bar': true
  });

  mainWindow.loadUrl('file://' + __dirname + '/imageviewer.html');

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


<commit_before>process.env.ELECTRON_HIDE_INTERNAL_MODULES = 'true';

const electron = require('electron');

const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Shell = electron.shell;
const dialog = electron.dialog;

const path = require('path');
const FS = require('fs');

const ConfigStore = require('configstore');
const Config = require('electron-config');
const ContextMenu = require('./context_menu');
const Menu = require('./menu');
const Tray = electron.Tray;
const SquirrelWindows = require('./squirrel_windows');
const auto_updater = require('./auto_update.js');

const _ = require('lodash');
require('electron-dl')();
const log = require('electron-log');
log.transports.file.level = 'silly';

if (SquirrelWindows.handleStartupEvent()) {
  process.exit();
}

const isMac = process.platform === 'darwin';
const isWin = process.platform === 'win32';

var mainWindow = null;
var menu = null;
var appIcon = null;

function setupConfig () {
  let defaults = {
    'host': 'https://www.irccloud.com',
    'width': 1024,
    'height': 768,
    'zoom': 0,
    'spellcheck': true,
    'neverPromptIrcUrls': false
  };

  // Migrate from old config, remove this in time
  let oldConfig = new ConfigStore(app.getName());
  try {
    Object.keys(defaults).concat('x', 'y').forEach(key => {
      if (oldConfig.has(key)) {
        defaults[key] = oldConfig.get(key);
      }
    });
    FS.unlinkSync(oldConfig.path);
  } catch (exc) {
    // noop
  }

  return new Config({
    defaults: defaults
  });
}
const config = setupConfig();

const minZoom = -8;
const maxZoom = 9;

app.config = config;
global.config = config;

var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
  openMainWindow();
  return true;
});
if (shouldQuit) {
  app.quit();
  process.exit();
}

function enableStreamlinedLogin() {
  // Enable the streamlined login page
  var inviteCookie = { url : config.get('host'), name : "invite", value : "1" };
  mainWindow.webContents.session.cookies.set(inviteCookie, function (error) {
    if (error) {
      log.error('set invite cookie error', error);
    }
  });
}

function openMainWindow() {
  // Someone tried to run a second instance, we should focus our window
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }
    mainWindow.show();
    mainWindow.focus();
    return;
  }

  var windowOpts = {
    'icon': path.join(__dirname, isWin ? 'icon.ico' : 'icon.png'),
    'width': config.get('width'),
    'height': config.get('height'),
    'webPreferences': {
      'allowDisplayingInsecureContent': true,
      'preload': path.join(__dirname, 'render', 'preload.js'),
      'nodeIntegration': false
    },
    'title': app.getName()
  };
  if (config.has('x') && config.has('y')) {
    windowOpts.x = config.get('x');
    windowOpts.y = config.get('y');
  }
  mainWindow = new BrowserWindow(windowOpts);
  if (config.get('maximize') === true) {
    mainWindow.maximize();
  }

  var initialUrl = config.get('host') + '/';
  if (ircUrlOnOpen) {
    initialUrl += '#?/irc_url=' + ircUrlOnOpen;
    ircUrlOnOpen = null;
  }

  enableStreamlinedLogin();
  mainWindow.loadURL(initialUrl);

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

  mainWindow.on('swipe', function (e, direction) {
    switch (direction) {
    case 'right':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    case 'left':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    }
  });

  mainWindow.on('page-title-updated', function(event) {
    var title = mainWindow.getTitle();
    if (title && isMac) {
      var unread = "";
      var matches = title.match(/^\((\d+)\)/);
      if (matches) {
        unread = matches[1];
      }
      app.dock.setBadge(unread);
    }
  });

  mainWindow.on('app-command', function (e, cmd) {
    switch (cmd) {
    case 'browser-backward':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    case 'browser-forward':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    case 'browser-refresh':
      mainWindow.webContents.reloadIgnoringCache();
      break;
    default:
      break;
    }
  });

  ContextMenu(mainWindow);

  mainWindow.webContents.on('dom-ready', function(event) {
    var userStylePath = path.join(app.getPath('userData'), 'user-style.css');
    FS.readFile(userStylePath, 'utf8', function (err, data) {
      if (!err) {
        mainWindow.webContents.insertCSS(data);
      }
    });
  });

  mainWindow.webContents.on('will-navigate', function (e, url) {
    // Make sure the invite cookie persists over logout
    enableStreamlinedLogin();
  });
  mainWindow.webContents.session.on('will-download', function (e, item, webContents) {
    if (manualDownload && manualDownload.saveAs) {
      item.setSavePath('');
    }
    item.on('done', function (e, state) {
      manualDownload = null;
    });
  });

  mainWindow.webContents.on('did-navigate-in-page', function (e, url) {
    var historyMenu = menu.items.find(function (item) {
      return item.id == 'history';
    });
    historyMenu.submenu.items.forEach(function (historyItem) {
      switch (historyItem.id) {
      case 'backMenu':
        historyItem.enabled = mainWindow.webContents.canGoBack();
        break;
      case 'fwdMenu':
        historyItem.enabled = mainWindow.webContents.canGoForward();
        break;
      default:
        break;
      }
    });
  });

  mainWindow.webContents.on('new-window', function(event, url, frameName, disposition) {
    event.preventDefault();
    var activate = disposition != 'background-tab';
    Shell.openExternal(url, {
      activate: activate
    });
  });
  mainWindow.on('close', function(ev) {
    var size = mainWindow.getSize();
    var position = mainWindow.getPosition();
    var props = {
      'width': size[0],
      'height': size[1],
      'x': position[0],
      'y': position[1]
    };
    if (mainWindow.isMaximized()) {
      props.maximized = true;
    }
    config.set(props);
    if (!quitting && (isMac || config.get('tray'))) {
      ev.preventDefault();
      mainWindow.hide();
    }
  });
}

var quitting = false;
app.on('activate', openMainWindow);
app.on('before-quit', function () {
  quitting = true;
});
if (!isMac) {
  app.on('window-all-closed', function() {
    app.quit();
  });
}

// Handles urls from app.isDefaultProtocolClient
app.on('open-url', function (event, url) {
  if (mainWindow) {
    mainWindow.webContents.send('set-irc-url', url);
  } else {
    ircUrlOnOpen = url;
  }
});

function destroyTray() {
  if (appIcon) {
    appIcon.destroy();
  }
}

function setupTray() {
  appIcon = new Tray(path.join(__dirname, isWin ? 'icon.ico' : 'icon.png'));
  appIcon.setToolTip(app.getName());
  appIcon.on('click', function() {
    openMainWindow();
  });
  var tray_menu = Menu.setup_tray(app);
  appIcon.setContextMenu(tray_menu);
}

var manualDownload;
app.doDownload = function (win, url, opts) {
  manualDownload = opts;
  win.webContents.downloadURL(url);
};

app.toggleTray = function() {
  if (config.get('tray')) {
    return setupTray();
  } else {
    return destroyTray();
  }
};
app.toggleSpellcheck = function() {
  if (config.get('spellcheck')) {
    mainWindow.webContents.send('enable-spellcheck');
  } else {
    mainWindow.webContents.send('disable-spellcheck');
  }
};

function updateZoomMenu() {
  var zoomLevel = config.get('zoom');
  var viewMenu = menu.items.find(function (item) {
    return item.id == 'view';
  });
  viewMenu.submenu.items.forEach(function (viewItem) {
    switch (viewItem.id) {
    case 'zoomReset':
      viewItem.enabled = zoomLevel !== 0;
      break;
    case 'zoomIn':
      viewItem.enabled = zoomLevel < maxZoom;
      break;
    case 'zoomOut':
      viewItem.enabled = zoomLevel > minZoom;
      break;
    default:
      break;
    }
  });
}

function updateZoom(zoomLevel) {
  config.set('zoom', zoomLevel);
  if (mainWindow) {
    mainWindow.webContents.send('update-zoom-level');
  }
  updateZoomMenu();
}

app.zoomIn = function () {
  var newZoom = Math.min(maxZoom, config.get('zoom') + 1);
  updateZoom(newZoom);
};
app.zoomOut = function () {
  var newZoom = Math.max(minZoom, config.get('zoom') - 1);
  updateZoom(newZoom);
};
app.resetZoom = function () {
  updateZoom(0);
};

function hideMenuBar(window) {
  window.setAutoHideMenuBar(true);
  window.setMenuBarVisibility(false);
}

function showMenuBar(window) {
  window.setAutoHideMenuBar(false);
  window.setMenuBarVisibility(true);
}

app.toggleMenuBar = function (window) {
  if (config.get('menu-bar')) {
    return showMenuBar(window);
  } else {
    return hideMenuBar(window);
  }
};

// Handle irc URLs
var ircUrlOnOpen;
function handleProtocolUrls () {
  // https://github.com/electron/electron/blob/master/docs/api/app.md#appsetasdefaultprotocolclientprotocol-macos-windows
  var isHandler = (
    app.isDefaultProtocolClient('irc') &&
    app.isDefaultProtocolClient('ircs')
    );

  if (!isHandler && !config.get('neverPromptIrcUrls')) {
    dialog.showMessageBox({
      type: 'info',
      message: 'Would you like to set ' + app.getName() + ' as your default IRC client?',
      buttons: ['Set Default', 'Not Now', 'Don’t Ask Again'],
      cancelId: 1,
      defaultId: 0
    }, function (ret) {
      switch (ret) {
      case 0:
        app.setAsDefaultProtocolClient('irc');
        app.setAsDefaultProtocolClient('ircs');
        handleProtocolUrls();
        break;
      case 2:
        config.set('neverPromptIrcUrls', true);
        break;
      }
    });
  }
}

app.on('ready', function() {
  handleProtocolUrls();

  menu = Menu.setup(config);
  auto_updater.setup(menu);
  updateZoomMenu();
  openMainWindow();
  if (config.get('tray') && !isMac) {
    setupTray();
  }
  if (config.get('menu-bar') === false && !isMac) {
    hideMenuBar(mainWindow);
  }
});
<commit_msg>Only show set default IRC client on Mac/Windows<commit_after>process.env.ELECTRON_HIDE_INTERNAL_MODULES = 'true';

const electron = require('electron');

const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Shell = electron.shell;
const dialog = electron.dialog;

const path = require('path');
const FS = require('fs');

const ConfigStore = require('configstore');
const Config = require('electron-config');
const ContextMenu = require('./context_menu');
const Menu = require('./menu');
const Tray = electron.Tray;
const SquirrelWindows = require('./squirrel_windows');
const auto_updater = require('./auto_update.js');

const _ = require('lodash');
require('electron-dl')();
const log = require('electron-log');
log.transports.file.level = 'silly';

if (SquirrelWindows.handleStartupEvent()) {
  process.exit();
}

const isMac = process.platform === 'darwin';
const isWin = process.platform === 'win32';

var mainWindow = null;
var menu = null;
var appIcon = null;

function setupConfig () {
  let defaults = {
    'host': 'https://www.irccloud.com',
    'width': 1024,
    'height': 768,
    'zoom': 0,
    'spellcheck': true,
    'neverPromptIrcUrls': false
  };

  // Migrate from old config, remove this in time
  let oldConfig = new ConfigStore(app.getName());
  try {
    Object.keys(defaults).concat('x', 'y').forEach(key => {
      if (oldConfig.has(key)) {
        defaults[key] = oldConfig.get(key);
      }
    });
    FS.unlinkSync(oldConfig.path);
  } catch (exc) {
    // noop
  }

  return new Config({
    defaults: defaults
  });
}
const config = setupConfig();

const minZoom = -8;
const maxZoom = 9;

app.config = config;
global.config = config;

var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
  openMainWindow();
  return true;
});
if (shouldQuit) {
  app.quit();
  process.exit();
}

function enableStreamlinedLogin() {
  // Enable the streamlined login page
  var inviteCookie = { url : config.get('host'), name : "invite", value : "1" };
  mainWindow.webContents.session.cookies.set(inviteCookie, function (error) {
    if (error) {
      log.error('set invite cookie error', error);
    }
  });
}

function openMainWindow() {
  // Someone tried to run a second instance, we should focus our window
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }
    mainWindow.show();
    mainWindow.focus();
    return;
  }

  var windowOpts = {
    'icon': path.join(__dirname, isWin ? 'icon.ico' : 'icon.png'),
    'width': config.get('width'),
    'height': config.get('height'),
    'webPreferences': {
      'allowDisplayingInsecureContent': true,
      'preload': path.join(__dirname, 'render', 'preload.js'),
      'nodeIntegration': false
    },
    'title': app.getName()
  };
  if (config.has('x') && config.has('y')) {
    windowOpts.x = config.get('x');
    windowOpts.y = config.get('y');
  }
  mainWindow = new BrowserWindow(windowOpts);
  if (config.get('maximize') === true) {
    mainWindow.maximize();
  }

  var initialUrl = config.get('host') + '/';
  if (ircUrlOnOpen) {
    initialUrl += '#?/irc_url=' + ircUrlOnOpen;
    ircUrlOnOpen = null;
  }

  enableStreamlinedLogin();
  mainWindow.loadURL(initialUrl);

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

  mainWindow.on('swipe', function (e, direction) {
    switch (direction) {
    case 'right':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    case 'left':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    }
  });

  mainWindow.on('page-title-updated', function(event) {
    var title = mainWindow.getTitle();
    if (title && isMac) {
      var unread = "";
      var matches = title.match(/^\((\d+)\)/);
      if (matches) {
        unread = matches[1];
      }
      app.dock.setBadge(unread);
    }
  });

  mainWindow.on('app-command', function (e, cmd) {
    switch (cmd) {
    case 'browser-backward':
      if (mainWindow.webContents.canGoBack()) {
        mainWindow.webContents.goBack();
      }
      break;
    case 'browser-forward':
      if (mainWindow.webContents.canGoForward()) {
        mainWindow.webContents.goForward();
      }
      break;
    case 'browser-refresh':
      mainWindow.webContents.reloadIgnoringCache();
      break;
    default:
      break;
    }
  });

  ContextMenu(mainWindow);

  mainWindow.webContents.on('dom-ready', function(event) {
    var userStylePath = path.join(app.getPath('userData'), 'user-style.css');
    FS.readFile(userStylePath, 'utf8', function (err, data) {
      if (!err) {
        mainWindow.webContents.insertCSS(data);
      }
    });
  });

  mainWindow.webContents.on('will-navigate', function (e, url) {
    // Make sure the invite cookie persists over logout
    enableStreamlinedLogin();
  });
  mainWindow.webContents.session.on('will-download', function (e, item, webContents) {
    if (manualDownload && manualDownload.saveAs) {
      item.setSavePath('');
    }
    item.on('done', function (e, state) {
      manualDownload = null;
    });
  });

  mainWindow.webContents.on('did-navigate-in-page', function (e, url) {
    var historyMenu = menu.items.find(function (item) {
      return item.id == 'history';
    });
    historyMenu.submenu.items.forEach(function (historyItem) {
      switch (historyItem.id) {
      case 'backMenu':
        historyItem.enabled = mainWindow.webContents.canGoBack();
        break;
      case 'fwdMenu':
        historyItem.enabled = mainWindow.webContents.canGoForward();
        break;
      default:
        break;
      }
    });
  });

  mainWindow.webContents.on('new-window', function(event, url, frameName, disposition) {
    event.preventDefault();
    var activate = disposition != 'background-tab';
    Shell.openExternal(url, {
      activate: activate
    });
  });
  mainWindow.on('close', function(ev) {
    var size = mainWindow.getSize();
    var position = mainWindow.getPosition();
    var props = {
      'width': size[0],
      'height': size[1],
      'x': position[0],
      'y': position[1]
    };
    if (mainWindow.isMaximized()) {
      props.maximized = true;
    }
    config.set(props);
    if (!quitting && (isMac || config.get('tray'))) {
      ev.preventDefault();
      mainWindow.hide();
    }
  });
}

var quitting = false;
app.on('activate', openMainWindow);
app.on('before-quit', function () {
  quitting = true;
});
if (!isMac) {
  app.on('window-all-closed', function() {
    app.quit();
  });
}

// Handles urls from app.isDefaultProtocolClient
app.on('open-url', function (event, url) {
  if (mainWindow) {
    mainWindow.webContents.send('set-irc-url', url);
  } else {
    ircUrlOnOpen = url;
  }
});

function destroyTray() {
  if (appIcon) {
    appIcon.destroy();
  }
}

function setupTray() {
  appIcon = new Tray(path.join(__dirname, isWin ? 'icon.ico' : 'icon.png'));
  appIcon.setToolTip(app.getName());
  appIcon.on('click', function() {
    openMainWindow();
  });
  var tray_menu = Menu.setup_tray(app);
  appIcon.setContextMenu(tray_menu);
}

var manualDownload;
app.doDownload = function (win, url, opts) {
  manualDownload = opts;
  win.webContents.downloadURL(url);
};

app.toggleTray = function() {
  if (config.get('tray')) {
    return setupTray();
  } else {
    return destroyTray();
  }
};
app.toggleSpellcheck = function() {
  if (config.get('spellcheck')) {
    mainWindow.webContents.send('enable-spellcheck');
  } else {
    mainWindow.webContents.send('disable-spellcheck');
  }
};

function updateZoomMenu() {
  var zoomLevel = config.get('zoom');
  var viewMenu = menu.items.find(function (item) {
    return item.id == 'view';
  });
  viewMenu.submenu.items.forEach(function (viewItem) {
    switch (viewItem.id) {
    case 'zoomReset':
      viewItem.enabled = zoomLevel !== 0;
      break;
    case 'zoomIn':
      viewItem.enabled = zoomLevel < maxZoom;
      break;
    case 'zoomOut':
      viewItem.enabled = zoomLevel > minZoom;
      break;
    default:
      break;
    }
  });
}

function updateZoom(zoomLevel) {
  config.set('zoom', zoomLevel);
  if (mainWindow) {
    mainWindow.webContents.send('update-zoom-level');
  }
  updateZoomMenu();
}

app.zoomIn = function () {
  var newZoom = Math.min(maxZoom, config.get('zoom') + 1);
  updateZoom(newZoom);
};
app.zoomOut = function () {
  var newZoom = Math.max(minZoom, config.get('zoom') - 1);
  updateZoom(newZoom);
};
app.resetZoom = function () {
  updateZoom(0);
};

function hideMenuBar(window) {
  window.setAutoHideMenuBar(true);
  window.setMenuBarVisibility(false);
}

function showMenuBar(window) {
  window.setAutoHideMenuBar(false);
  window.setMenuBarVisibility(true);
}

app.toggleMenuBar = function (window) {
  if (config.get('menu-bar')) {
    return showMenuBar(window);
  } else {
    return hideMenuBar(window);
  }
};

// Handle irc URLs
var ircUrlOnOpen;
function handleProtocolUrls () {
  // https://github.com/electron/electron/blob/master/docs/api/app.md#appsetasdefaultprotocolclientprotocol-macos-windows
  var supported = isMac || isWin;
  if (!supported) {
    return;
  }
  var isHandler = (
    app.isDefaultProtocolClient('irc') &&
    app.isDefaultProtocolClient('ircs')
    );

  if (!isHandler && !config.get('neverPromptIrcUrls')) {
    dialog.showMessageBox({
      type: 'info',
      message: 'Would you like to set ' + app.getName() + ' as your default IRC client?',
      buttons: ['Set Default', 'Not Now', 'Don’t Ask Again'],
      cancelId: 1,
      defaultId: 0
    }, function (ret) {
      switch (ret) {
      case 0:
        app.setAsDefaultProtocolClient('irc');
        app.setAsDefaultProtocolClient('ircs');
        handleProtocolUrls();
        break;
      case 2:
        config.set('neverPromptIrcUrls', true);
        break;
      }
    });
  }
}

app.on('ready', function() {
  handleProtocolUrls();

  menu = Menu.setup(config);
  auto_updater.setup(menu);
  updateZoomMenu();
  openMainWindow();
  if (config.get('tray') && !isMac) {
    setupTray();
  }
  if (config.get('menu-bar') === false && !isMac) {
    hideMenuBar(mainWindow);
  }
});
<|endoftext|>