<commit_before>#!/usr/bin/env node
"use strict";

const path = require("path");
const open = require("opn");
const fs = require("fs");
const net = require("net");
const url = require("url");
const portfinder = require("portfinder");

// Local version replaces global one
try {
	const localWebpackDevServer = require.resolve(path.join(process.cwd(), "node_modules", "webpack-dev-server", "bin", "webpack-dev-server.js"));
	if(__filename !== localWebpackDevServer) {
		return require(localWebpackDevServer);
	}
} catch(e) {}

const Server = require("../lib/Server");
const webpack = require("webpack");

function versionInfo() {
	return `webpack-dev-server ${require("../package.json").version}\n` +
		`webpack ${require("webpack/package.json").version}`;
}

function colorInfo(useColor, msg) {
	if(useColor)
		// Make text blue and bold, so it *pops*
		return `\u001b[1m\u001b[34m${msg}\u001b[39m\u001b[22m`;
	return msg;
}

function colorError(useColor, msg) {
	if(useColor)
		// Make text red and bold, so it *pops*
		return `\u001b[1m\u001b[31m${msg}\u001b[39m\u001b[22m`;
	return msg;
}

const yargs = require("yargs")
	.usage(`${versionInfo()
		}\nUsage: https://webpack.js.org/configuration/dev-server/`);

require("webpack/bin/config-yargs")(yargs);

// It is important that this is done after the webpack yargs config,
// so it overrides webpack's version info.
yargs.version(versionInfo);

const ADVANCED_GROUP = "Advanced options:";
const DISPLAY_GROUP = "Stats options:";
const SSL_GROUP = "SSL options:";
const CONNECTION_GROUP = "Connection options:";
const RESPONSE_GROUP = "Response options:";
const BASIC_GROUP = "Basic options:";

// Taken out of yargs because we must know if
// it wasn't given by the user, in which case
// we should use portfinder.
const DEFAULT_PORT = 8080;

yargs.options({
	"lazy": {
		type: "boolean",
		describe: "Lazy"
	},
	"inline": {
		type: "boolean",
		default: true,
		describe: "Inline mode (set to false to disable including client scripts like livereload)"
	},
	"progress": {
		type: "boolean",
		describe: "Print compilation progress in percentage",
		group: BASIC_GROUP
	},
	"hot-only": {
		type: "boolean",
		describe: "Do not refresh page if HMR fails",
		group: ADVANCED_GROUP
	},
	"stdin": {
		type: "boolean",
		describe: "close when stdin ends"
	},
	"open": {
		type: "boolean",
		describe: "Open default browser"
	},
	"color": {
		type: "boolean",
		alias: "colors",
		default: function supportsColor() {
			return require("supports-color");
		},
		group: DISPLAY_GROUP,
		describe: "Enables/Disables colors on the console"
	},
	"info": {
		type: "boolean",
		group: DISPLAY_GROUP,
		default: true,
		describe: "Info"
	},
	"quiet": {
		type: "boolean",
		group: DISPLAY_GROUP,
		describe: "Quiet"
	},
	"client-log-level": {
		type: "string",
		group: DISPLAY_GROUP,
		default: "info",
		describe: "Log level in the browser (info, warning, error or none)"
	},
	"https": {
		type: "boolean",
		group: SSL_GROUP,
		describe: "HTTPS"
	},
	"key": {
		type: "string",
		describe: "Path to a SSL key.",
		group: SSL_GROUP
	},
	"cert": {
		type: "string",
		describe: "Path to a SSL certificate.",
		group: SSL_GROUP
	},
	"cacert": {
		type: "string",
		describe: "Path to a SSL CA certificate.",
		group: SSL_GROUP
	},
	"pfx": {
		type: "string",
		describe: "Path to a SSL pfx file.",
		group: SSL_GROUP
	},
	"pfx-passphrase": {
		type: "string",
		describe: "Passphrase for pfx file.",
		group: SSL_GROUP
	},
	"content-base": {
		type: "string",
		describe: "A directory or URL to serve HTML content from.",
		group: RESPONSE_GROUP
	},
	"watch-content-base": {
		type: "boolean",
		describe: "Enable live-reloading of the content-base.",
		group: RESPONSE_GROUP
	},
	"history-api-fallback": {
		type: "boolean",
		describe: "Fallback to /index.html for Single Page Applications.",
		group: RESPONSE_GROUP
	},
	"compress": {
		type: "boolean",
		describe: "Enable gzip compression",
		group: RESPONSE_GROUP
	},
	"port": {
		describe: "The port",
		group: CONNECTION_GROUP
	},
	"socket": {
		type: "String",
		describe: "Socket to listen",
		group: CONNECTION_GROUP
	},
	"public": {
		type: "string",
		describe: "The public hostname/ip address of the server",
		group: CONNECTION_GROUP
	},
	"host": {
		type: "string",
		default: "localhost",
		describe: "The hostname/ip address the server will bind to",
		group: CONNECTION_GROUP
	}
});

const argv = yargs.argv;

const wpOpt = require("webpack/bin/convert-argv")(yargs, argv, {
	outputFilename: "/bundle.js"
});

function processOptions(wpOpt) {
	// process Promise
	if(typeof wpOpt.then === "function") {
		wpOpt.then(processOptions).catch(function(err) {
			console.error(err.stack || err);
			process.exit(); // eslint-disable-line
		});
		return;
	}

	const firstWpOpt = Array.isArray(wpOpt) ? wpOpt[0] : wpOpt;

	const options = wpOpt.devServer || firstWpOpt.devServer || {};

	if(argv.host !== "localhost" || !options.host)
		options.host = argv.host;

	if(argv.public)
		options.public = argv.public;

	if(argv.socket)
		options.socket = argv.socket;

	if(!options.publicPath) {
		options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || "";
		if(!/^(https?:)?\/\//.test(options.publicPath) && options.publicPath[0] !== "/")
			options.publicPath = `/${options.publicPath}`;
	}

	if(!options.filename)
		options.filename = firstWpOpt.output && firstWpOpt.output.filename;

	if(!options.watchOptions)
		options.watchOptions = firstWpOpt.watchOptions;

	if(argv["stdin"]) {
		process.stdin.on("end", function() {
			process.exit(0); // eslint-disable-line no-process-exit
		});
		process.stdin.resume();
	}

	if(!options.hot)
		options.hot = argv["hot"];

	if(!options.hotOnly)
		options.hotOnly = argv["hot-only"];

	if(!options.clientLogLevel)
		options.clientLogLevel = argv["client-log-level"];

	if(options.contentBase === undefined) {
		if(argv["content-base"]) {
			options.contentBase = argv["content-base"];
			if(/^[0-9]$/.test(options.contentBase))
				options.contentBase = +options.contentBase;
			else if(!/^(https?:)?\/\//.test(options.contentBase))
				options.contentBase = path.resolve(options.contentBase);
		// It is possible to disable the contentBase by using `--no-content-base`, which results in arg["content-base"] = false
		} else if(argv["content-base"] === false) {
			options.contentBase = false;
		}
	}

	if(argv["watch-content-base"])
		options.watchContentBase = true;

	if(!options.stats) {
		options.stats = {
			cached: false,
			cachedAssets: false
		};
	}

	if(typeof options.stats === "object" && typeof options.stats.colors === "undefined")
		options.stats.colors = argv.color;

	if(argv["lazy"])
		options.lazy = true;

	if(!argv["info"])
		options.noInfo = true;

	if(argv["quiet"])
		options.quiet = true;

	if(argv["https"])
		options.https = true;

	if(argv["cert"])
		options.cert = fs.readFileSync(path.resolve(argv["cert"]));

	if(argv["key"])
		options.key = fs.readFileSync(path.resolve(argv["key"]));

	if(argv["cacert"])
		options.ca = fs.readFileSync(path.resolve(argv["cacert"]));

	if(argv["pfx"])
		options.pfx = fs.readFileSync(path.resolve(argv["pfx"]));

	if(argv["pfx-passphrase"])
		options.pfxPassphrase = argv["pfx-passphrase"];

	if(argv["inline"] === false)
		options.inline = false;

	if(argv["history-api-fallback"])
		options.historyApiFallback = true;

	if(argv["compress"])
		options.compress = true;

	if(argv["open"])
		options.open = true;

	// Kind of weird, but ensures prior behavior isn't broken in cases
	// that wouldn't throw errors. E.g. both argv.port and options.port
	// were specified, but since argv.port is 8080, options.port will be
	// tried first instead.
	options.port = argv.port === DEFAULT_PORT ? (options.port || argv.port) : (argv.port || options.port);
	if(options.port) {
		startDevServer(wpOpt, options);
		return;
	}

	portfinder.basePort = DEFAULT_PORT;
	portfinder.getPort(function(err, port) {
		if(err) throw err;
		options.port = port;
		startDevServer(wpOpt, options);
	});
}

function startDevServer(wpOpt, options) {
	const protocol = options.https ? "https" : "http";

	// the formatted domain (url without path) of the webpack server
	const domain = options.public ? `${protocol}://${options.public}` : url.format({
		protocol: protocol,
		hostname: options.host,
		port: options.socket ? 0 : options.port.toString()
	});

	if(options.inline !== false) {
		const devClient = [`${require.resolve("../client/")}?${domain}`];

		if(options.hotOnly)
			devClient.push("webpack/hot/only-dev-server");
		else if(options.hot)
			devClient.push("webpack/hot/dev-server");

		[].concat(wpOpt).forEach(function(wpOpt) {
			if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) {
				Object.keys(wpOpt.entry).forEach(function(key) {
					wpOpt.entry[key] = devClient.concat(wpOpt.entry[key]);
				});
			} else {
				wpOpt.entry = devClient.concat(wpOpt.entry);
			}
		});
	}

	let compiler;
	try {
		compiler = webpack(wpOpt);
	} catch(e) {
		if(e instanceof webpack.WebpackOptionsValidationError) {
			console.error(colorError(options.stats.colors, e.message));
			process.exit(1); // eslint-disable-line
		}
		throw e;
	}

	if(argv["progress"]) {
		compiler.apply(new webpack.ProgressPlugin({
			profile: argv["profile"]
		}));
	}

	const uri = domain + (options.inline !== false || options.lazy === true ? "/" : "/webpack-dev-server/");

	let server;
	try {
		server = new Server(compiler, options);
	} catch(e) {
		const OptionsValidationError = require("../lib/OptionsValidationError");
		if(e instanceof OptionsValidationError) {
			console.error(colorError(options.stats.colors, e.message));
			process.exit(1); // eslint-disable-line
		}
		throw e;
	}

	if(options.socket) {
		server.listeningApp.on("error", function(e) {
			if(e.code === "EADDRINUSE") {
				const clientSocket = new net.Socket();
				clientSocket.on("error", function(e) {
					if(e.code === "ECONNREFUSED") {
						// No other server listening on this socket so it can be safely removed
						fs.unlinkSync(options.socket);
						server.listen(options.socket, options.host, function(err) {
							if(err) throw err;
						});
					}
				});
				clientSocket.connect({ path: options.socket }, function() {
					throw new Error("This socket is already used");
				});
			}
		});
		server.listen(options.socket, options.host, function(err) {
			if(err) throw err;
			const READ_WRITE = 438; // chmod 666 (rw rw rw)
			fs.chmod(options.socket, READ_WRITE, function(err) {
				if(err) throw err;
				reportReadiness(uri, options);
			});
		});
	} else {
		server.listen(options.port, options.host, function(err) {
			if(err) throw err;
			reportReadiness(uri, options);
		});
	}
}

function reportReadiness(uri, options) {
	const useColor = argv.color;
	let startSentence = `Project is running at ${colorInfo(useColor, uri)}`
	if(options.socket) {
		startSentence = `Listening to socket at ${colorInfo(useColor, options.socket)}`;
	}
	console.log((argv["progress"] ? "\n" : "") + startSentence);

	console.log(`webpack output is served from ${colorInfo(useColor, options.publicPath)}`);
	const contentBase = Array.isArray(options.contentBase) ? options.contentBase.join(", ") : options.contentBase;
	if(contentBase)
		console.log(`Content not from webpack is served from ${colorInfo(useColor, contentBase)}`);
	if(options.historyApiFallback)
		console.log(`404s will fallback to ${colorInfo(useColor, options.historyApiFallback.index || "/index.html")}`);
	if(options.open)
		open(uri);
}

processOptions(wpOpt);
<commit_msg>Handle promise rejection when using `--open` flag (#780)<commit_after>#!/usr/bin/env node
"use strict";

const path = require("path");
const open = require("opn");
const fs = require("fs");
const net = require("net");
const url = require("url");
const portfinder = require("portfinder");

// Local version replaces global one
try {
	const localWebpackDevServer = require.resolve(path.join(process.cwd(), "node_modules", "webpack-dev-server", "bin", "webpack-dev-server.js"));
	if(__filename !== localWebpackDevServer) {
		return require(localWebpackDevServer);
	}
} catch(e) {}

const Server = require("../lib/Server");
const webpack = require("webpack");

function versionInfo() {
	return `webpack-dev-server ${require("../package.json").version}\n` +
		`webpack ${require("webpack/package.json").version}`;
}

function colorInfo(useColor, msg) {
	if(useColor)
		// Make text blue and bold, so it *pops*
		return `\u001b[1m\u001b[34m${msg}\u001b[39m\u001b[22m`;
	return msg;
}

function colorError(useColor, msg) {
	if(useColor)
		// Make text red and bold, so it *pops*
		return `\u001b[1m\u001b[31m${msg}\u001b[39m\u001b[22m`;
	return msg;
}

const yargs = require("yargs")
	.usage(`${versionInfo()
		}\nUsage: https://webpack.js.org/configuration/dev-server/`);

require("webpack/bin/config-yargs")(yargs);

// It is important that this is done after the webpack yargs config,
// so it overrides webpack's version info.
yargs.version(versionInfo);

const ADVANCED_GROUP = "Advanced options:";
const DISPLAY_GROUP = "Stats options:";
const SSL_GROUP = "SSL options:";
const CONNECTION_GROUP = "Connection options:";
const RESPONSE_GROUP = "Response options:";
const BASIC_GROUP = "Basic options:";

// Taken out of yargs because we must know if
// it wasn't given by the user, in which case
// we should use portfinder.
const DEFAULT_PORT = 8080;

yargs.options({
	"lazy": {
		type: "boolean",
		describe: "Lazy"
	},
	"inline": {
		type: "boolean",
		default: true,
		describe: "Inline mode (set to false to disable including client scripts like livereload)"
	},
	"progress": {
		type: "boolean",
		describe: "Print compilation progress in percentage",
		group: BASIC_GROUP
	},
	"hot-only": {
		type: "boolean",
		describe: "Do not refresh page if HMR fails",
		group: ADVANCED_GROUP
	},
	"stdin": {
		type: "boolean",
		describe: "close when stdin ends"
	},
	"open": {
		type: "boolean",
		describe: "Open default browser"
	},
	"color": {
		type: "boolean",
		alias: "colors",
		default: function supportsColor() {
			return require("supports-color");
		},
		group: DISPLAY_GROUP,
		describe: "Enables/Disables colors on the console"
	},
	"info": {
		type: "boolean",
		group: DISPLAY_GROUP,
		default: true,
		describe: "Info"
	},
	"quiet": {
		type: "boolean",
		group: DISPLAY_GROUP,
		describe: "Quiet"
	},
	"client-log-level": {
		type: "string",
		group: DISPLAY_GROUP,
		default: "info",
		describe: "Log level in the browser (info, warning, error or none)"
	},
	"https": {
		type: "boolean",
		group: SSL_GROUP,
		describe: "HTTPS"
	},
	"key": {
		type: "string",
		describe: "Path to a SSL key.",
		group: SSL_GROUP
	},
	"cert": {
		type: "string",
		describe: "Path to a SSL certificate.",
		group: SSL_GROUP
	},
	"cacert": {
		type: "string",
		describe: "Path to a SSL CA certificate.",
		group: SSL_GROUP
	},
	"pfx": {
		type: "string",
		describe: "Path to a SSL pfx file.",
		group: SSL_GROUP
	},
	"pfx-passphrase": {
		type: "string",
		describe: "Passphrase for pfx file.",
		group: SSL_GROUP
	},
	"content-base": {
		type: "string",
		describe: "A directory or URL to serve HTML content from.",
		group: RESPONSE_GROUP
	},
	"watch-content-base": {
		type: "boolean",
		describe: "Enable live-reloading of the content-base.",
		group: RESPONSE_GROUP
	},
	"history-api-fallback": {
		type: "boolean",
		describe: "Fallback to /index.html for Single Page Applications.",
		group: RESPONSE_GROUP
	},
	"compress": {
		type: "boolean",
		describe: "Enable gzip compression",
		group: RESPONSE_GROUP
	},
	"port": {
		describe: "The port",
		group: CONNECTION_GROUP
	},
	"socket": {
		type: "String",
		describe: "Socket to listen",
		group: CONNECTION_GROUP
	},
	"public": {
		type: "string",
		describe: "The public hostname/ip address of the server",
		group: CONNECTION_GROUP
	},
	"host": {
		type: "string",
		default: "localhost",
		describe: "The hostname/ip address the server will bind to",
		group: CONNECTION_GROUP
	}
});

const argv = yargs.argv;

const wpOpt = require("webpack/bin/convert-argv")(yargs, argv, {
	outputFilename: "/bundle.js"
});

function processOptions(wpOpt) {
	// process Promise
	if(typeof wpOpt.then === "function") {
		wpOpt.then(processOptions).catch(function(err) {
			console.error(err.stack || err);
			process.exit(); // eslint-disable-line
		});
		return;
	}

	const firstWpOpt = Array.isArray(wpOpt) ? wpOpt[0] : wpOpt;

	const options = wpOpt.devServer || firstWpOpt.devServer || {};

	if(argv.host !== "localhost" || !options.host)
		options.host = argv.host;

	if(argv.public)
		options.public = argv.public;

	if(argv.socket)
		options.socket = argv.socket;

	if(!options.publicPath) {
		options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || "";
		if(!/^(https?:)?\/\//.test(options.publicPath) && options.publicPath[0] !== "/")
			options.publicPath = `/${options.publicPath}`;
	}

	if(!options.filename)
		options.filename = firstWpOpt.output && firstWpOpt.output.filename;

	if(!options.watchOptions)
		options.watchOptions = firstWpOpt.watchOptions;

	if(argv["stdin"]) {
		process.stdin.on("end", function() {
			process.exit(0); // eslint-disable-line no-process-exit
		});
		process.stdin.resume();
	}

	if(!options.hot)
		options.hot = argv["hot"];

	if(!options.hotOnly)
		options.hotOnly = argv["hot-only"];

	if(!options.clientLogLevel)
		options.clientLogLevel = argv["client-log-level"];

	if(options.contentBase === undefined) {
		if(argv["content-base"]) {
			options.contentBase = argv["content-base"];
			if(/^[0-9]$/.test(options.contentBase))
				options.contentBase = +options.contentBase;
			else if(!/^(https?:)?\/\//.test(options.contentBase))
				options.contentBase = path.resolve(options.contentBase);
		// It is possible to disable the contentBase by using `--no-content-base`, which results in arg["content-base"] = false
		} else if(argv["content-base"] === false) {
			options.contentBase = false;
		}
	}

	if(argv["watch-content-base"])
		options.watchContentBase = true;

	if(!options.stats) {
		options.stats = {
			cached: false,
			cachedAssets: false
		};
	}

	if(typeof options.stats === "object" && typeof options.stats.colors === "undefined")
		options.stats.colors = argv.color;

	if(argv["lazy"])
		options.lazy = true;

	if(!argv["info"])
		options.noInfo = true;

	if(argv["quiet"])
		options.quiet = true;

	if(argv["https"])
		options.https = true;

	if(argv["cert"])
		options.cert = fs.readFileSync(path.resolve(argv["cert"]));

	if(argv["key"])
		options.key = fs.readFileSync(path.resolve(argv["key"]));

	if(argv["cacert"])
		options.ca = fs.readFileSync(path.resolve(argv["cacert"]));

	if(argv["pfx"])
		options.pfx = fs.readFileSync(path.resolve(argv["pfx"]));

	if(argv["pfx-passphrase"])
		options.pfxPassphrase = argv["pfx-passphrase"];

	if(argv["inline"] === false)
		options.inline = false;

	if(argv["history-api-fallback"])
		options.historyApiFallback = true;

	if(argv["compress"])
		options.compress = true;

	if(argv["open"])
		options.open = true;

	// Kind of weird, but ensures prior behavior isn't broken in cases
	// that wouldn't throw errors. E.g. both argv.port and options.port
	// were specified, but since argv.port is 8080, options.port will be
	// tried first instead.
	options.port = argv.port === DEFAULT_PORT ? (options.port || argv.port) : (argv.port || options.port);
	if(options.port) {
		startDevServer(wpOpt, options);
		return;
	}

	portfinder.basePort = DEFAULT_PORT;
	portfinder.getPort(function(err, port) {
		if(err) throw err;
		options.port = port;
		startDevServer(wpOpt, options);
	});
}

function startDevServer(wpOpt, options) {
	const protocol = options.https ? "https" : "http";

	// the formatted domain (url without path) of the webpack server
	const domain = options.public ? `${protocol}://${options.public}` : url.format({
		protocol: protocol,
		hostname: options.host,
		port: options.socket ? 0 : options.port.toString()
	});

	if(options.inline !== false) {
		const devClient = [`${require.resolve("../client/")}?${domain}`];

		if(options.hotOnly)
			devClient.push("webpack/hot/only-dev-server");
		else if(options.hot)
			devClient.push("webpack/hot/dev-server");

		[].concat(wpOpt).forEach(function(wpOpt) {
			if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) {
				Object.keys(wpOpt.entry).forEach(function(key) {
					wpOpt.entry[key] = devClient.concat(wpOpt.entry[key]);
				});
			} else {
				wpOpt.entry = devClient.concat(wpOpt.entry);
			}
		});
	}

	let compiler;
	try {
		compiler = webpack(wpOpt);
	} catch(e) {
		if(e instanceof webpack.WebpackOptionsValidationError) {
			console.error(colorError(options.stats.colors, e.message));
			process.exit(1); // eslint-disable-line
		}
		throw e;
	}

	if(argv["progress"]) {
		compiler.apply(new webpack.ProgressPlugin({
			profile: argv["profile"]
		}));
	}

	const uri = domain + (options.inline !== false || options.lazy === true ? "/" : "/webpack-dev-server/");

	let server;
	try {
		server = new Server(compiler, options);
	} catch(e) {
		const OptionsValidationError = require("../lib/OptionsValidationError");
		if(e instanceof OptionsValidationError) {
			console.error(colorError(options.stats.colors, e.message));
			process.exit(1); // eslint-disable-line
		}
		throw e;
	}

	if(options.socket) {
		server.listeningApp.on("error", function(e) {
			if(e.code === "EADDRINUSE") {
				const clientSocket = new net.Socket();
				clientSocket.on("error", function(e) {
					if(e.code === "ECONNREFUSED") {
						// No other server listening on this socket so it can be safely removed
						fs.unlinkSync(options.socket);
						server.listen(options.socket, options.host, function(err) {
							if(err) throw err;
						});
					}
				});
				clientSocket.connect({ path: options.socket }, function() {
					throw new Error("This socket is already used");
				});
			}
		});
		server.listen(options.socket, options.host, function(err) {
			if(err) throw err;
			const READ_WRITE = 438; // chmod 666 (rw rw rw)
			fs.chmod(options.socket, READ_WRITE, function(err) {
				if(err) throw err;
				reportReadiness(uri, options);
			});
		});
	} else {
		server.listen(options.port, options.host, function(err) {
			if(err) throw err;
			reportReadiness(uri, options);
		});
	}
}

function reportReadiness(uri, options) {
	const useColor = argv.color;
	let startSentence = `Project is running at ${colorInfo(useColor, uri)}`
	if(options.socket) {
		startSentence = `Listening to socket at ${colorInfo(useColor, options.socket)}`;
	}
	console.log((argv["progress"] ? "\n" : "") + startSentence);

	console.log(`webpack output is served from ${colorInfo(useColor, options.publicPath)}`);
	const contentBase = Array.isArray(options.contentBase) ? options.contentBase.join(", ") : options.contentBase;
	if(contentBase)
		console.log(`Content not from webpack is served from ${colorInfo(useColor, contentBase)}`);
	if(options.historyApiFallback)
		console.log(`404s will fallback to ${colorInfo(useColor, options.historyApiFallback.index || "/index.html")}`);
	if(options.open) {
		open(uri).catch(function() {
			console.log("Unable to open browser. If you are running in a headless environment, please do not use the open flag.");
		});
	}
}

processOptions(wpOpt);
<|endoftext|>


<commit_before>dojo.provide("dojox.gfx.utils");

dojo.require("dojox.gfx");

(function(){
	var d = dojo, g = dojox.gfx, gu = g.utils;

	dojo.mixin(gu, {
		forEach: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object,
			/*Function|String|Array*/ f, /*Object?*/ o
		){
			o = o || d.global;
			f.call(o, object);
			if(object instanceof g.Surface || object instanceof g.Group){
				d.forEach(object.children, function(shape){
					gu.inspect(shape, f, o);
				});
			}
		},

		serialize: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object
		){
			var t = {}, v, isSurface = object instanceof g.Surface;
			if(isSurface || object instanceof g.Group){
				t.children = d.map(object.children, gu.serialize);
				if(isSurface){
					return t.children;	// Array
				}
			}else{
				t.shape = object.getShape();
			}
			if(object.getTransform){
				v = object.getTransform();
				if(v){ t.transform = v; }
			}
			if(object.getStroke){
				v = object.getStroke();
				if(v){ t.stroke = v; }
			}
			if(object.getFill){
				v = object.getFill();
				if(v){ t.fill = v; }
			}
			if(object.getFont){
				v = object.getFont();
				if(v){ t.font = v; }
			}
			return t;	// Object
		},

		toJson: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object,
			/* Boolean? */ prettyPrint
		){
			return d.toJson(gu.serialize(object), prettyPrint);	// String
		},

		deserialize: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ parent,
			/* dojox.gfx.Shape || Array */ object
		){
			if(object instanceof Array){
				return d.map(object, d.hitch(null, gu.deserialize, parent));	// Array
			}
			var shape = ("shape" in object) ? parent.createShape(object.shape) : parent.createGroup();
			if("transform" in object){
				shape.setTransform(object.transform);
			}
			if("stroke" in object){
				shape.setStroke(object.stroke);
			}
			if("fill" in object){
				shape.setFill(object.fill);
			}
			if("font" in object){
				shape.setFont(object.font);
			}
			if("children" in object){
				d.forEach(object.children, d.hitch(null, gu.deserialize, shape));
			}
			return shape;	// dojox.gfx.Shape
		},

		fromJson: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ parent,
			/* String */ json
		){
			return gu.deserialize(parent, d.fromJson(json));	// Array || dojox.gfx.Shape
		}
	});
})();
<commit_msg>gfx: one more typo fix, !strict, fixes #9146.<commit_after>dojo.provide("dojox.gfx.utils");

dojo.require("dojox.gfx");

(function(){
	var d = dojo, g = dojox.gfx, gu = g.utils;

	dojo.mixin(gu, {
		forEach: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object,
			/*Function|String|Array*/ f, /*Object?*/ o
		){
			o = o || d.global;
			f.call(o, object);
			if(object instanceof g.Surface || object instanceof g.Group){
				d.forEach(object.children, function(shape){
					gu.forEach(shape, f, o);
				});
			}
		},

		serialize: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object
		){
			var t = {}, v, isSurface = object instanceof g.Surface;
			if(isSurface || object instanceof g.Group){
				t.children = d.map(object.children, gu.serialize);
				if(isSurface){
					return t.children;	// Array
				}
			}else{
				t.shape = object.getShape();
			}
			if(object.getTransform){
				v = object.getTransform();
				if(v){ t.transform = v; }
			}
			if(object.getStroke){
				v = object.getStroke();
				if(v){ t.stroke = v; }
			}
			if(object.getFill){
				v = object.getFill();
				if(v){ t.fill = v; }
			}
			if(object.getFont){
				v = object.getFont();
				if(v){ t.font = v; }
			}
			return t;	// Object
		},

		toJson: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ object,
			/* Boolean? */ prettyPrint
		){
			return d.toJson(gu.serialize(object), prettyPrint);	// String
		},

		deserialize: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ parent,
			/* dojox.gfx.Shape || Array */ object
		){
			if(object instanceof Array){
				return d.map(object, d.hitch(null, gu.deserialize, parent));	// Array
			}
			var shape = ("shape" in object) ? parent.createShape(object.shape) : parent.createGroup();
			if("transform" in object){
				shape.setTransform(object.transform);
			}
			if("stroke" in object){
				shape.setStroke(object.stroke);
			}
			if("fill" in object){
				shape.setFill(object.fill);
			}
			if("font" in object){
				shape.setFont(object.font);
			}
			if("children" in object){
				d.forEach(object.children, d.hitch(null, gu.deserialize, shape));
			}
			return shape;	// dojox.gfx.Shape
		},

		fromJson: function(
			/* dojox.gfx.Surface || dojox.gfx.Shape */ parent,
			/* String */ json
		){
			return gu.deserialize(parent, d.fromJson(json));	// Array || dojox.gfx.Shape
		}
	});
})();
<|endoftext|>


<commit_before>/*jshint node:true*/

var fs          = require('fs-extra');
var path        = require('path');
var chalk       = require('chalk');
var EmberRouterGenerator = require('ember-router-generator');

module.exports = {
  description: 'Generates a route and a template, and registers the route with the router.',

  availableOptions: [
    {
      name: 'path',
      type: String,
      default: ''
    },
    {
      name: 'skip-router',
      type: Boolean,
      default: false
    }
  ],

  fileMapTokens: function() {
    var blueprint = this.lookupBlueprint('route')
    return blueprint.fileMapTokens.apply(blueprint, arguments);
  },

  shouldTouchRouter: function(name, options) {
    return this.lookupBlueprint('route').shouldTouchRouter(name, options);
  },

  shouldEntityTouchRouter: function(name, options) {
    return this.lookupBlueprint('route').shouldEntityTouchRouter(name, options);
  },

  afterInstall: function(options) {
    updateRouter.call(this, 'add', options);
  },

  afterUninstall: function(options) {
    updateRouter.call(this, 'remove', options);
  }
};

function updateRouter(action, options) {
  var entity = options.entity;
  var actionColorMap = {
    add: 'green',
    remove: 'red'
  };
  var color = actionColorMap[action] || 'gray';

  if (this.shouldTouchRouter(entity.name, options)) {
    writeRoute(action, entity.name, options);

    this.ui.writeLine('updating router');
    this._writeStatusToUI(chalk[color], action + ' route', entity.name);
  }
}

function findRouter(options) {
  var routerPathParts = [options.project.root];

  if (options.dummy && options.project.isEmberCLIAddon()) {
    routerPathParts = routerPathParts.concat(['tests', 'dummy', 'app', 'router.js']);
  } else {
    routerPathParts = routerPathParts.concat(['app', 'router.js']);
  }

  return routerPathParts;
}

function writeRoute(action, name, options) {
  var routerPath = path.join.apply(null, findRouter(options));
  var source = fs.readFileSync(routerPath, 'utf-8');

  var routes = new EmberRouterGenerator(source);
  var newRoutes = routes[action](name, options);

  fs.writeFileSync(routerPath, newRoutes.code());
}
<commit_msg>Update router<commit_after>/*jshint node:true*/

var fs          = require('fs-extra');
var path        = require('path');
var chalk       = require('chalk');
var EmberRouterGenerator = require('ember-router-generator');

module.exports = {
  description: 'Generates a route and a template, and registers the route with the router.',

  availableOptions: [
    {
      name: 'path',
      type: String,
      default: ''
    },
    {
      name: 'skip-router',
      type: Boolean,
      default: false
    }
  ],

  fileMapTokens: function() {
    var blueprint = this.lookupBlueprint('route')
    return blueprint.fileMapTokens.apply(blueprint, arguments);
  },

  shouldTouchRouter: function(name, options) {
    return this.lookupBlueprint('route').shouldTouchRouter(name, options);
  },

  shouldEntityTouchRouter: function(name, options) {
    return this.lookupBlueprint('route').shouldEntityTouchRouter(name, options);
  },

  afterInstall: function(options) {
    updateRouter.call(this, 'add', options);
  },

  afterUninstall: function(options) {
    updateRouter.call(this, 'remove', options);
  }
};

function updateRouter(action, options) {
  var entity = options.entity;
  var actionColorMap = {
    add: 'green',
    remove: 'red'
  };
  var color = actionColorMap[action] || 'gray';

  if (this.shouldTouchRouter(entity.name, options)) {
    writeRoute(action, entity.name, options);

    this.ui.writeLine('updating router');
    this._writeStatusToUI(chalk[color], action + ' route', entity.name);
  }
}

function findRouter(options) {
  var routerPathParts = [options.project.root];

  if (options.dummy && options.project.isEmberCLIAddon()) {
    routerPathParts = routerPathParts.concat(['tests', 'dummy', 'app', 'router.ts']);
  } else {
    routerPathParts = routerPathParts.concat(['app', 'router.ts']);
  }

  return routerPathParts;
}

function writeRoute(action, name, options) {
  var routerPath = path.join.apply(null, findRouter(options));
  var source = fs.readFileSync(routerPath, 'utf-8');

  var routes = new EmberRouterGenerator(source);
  var newRoutes = routes[action](name, options);

  fs.writeFileSync(routerPath, newRoutes.code());
}
<|endoftext|>


<commit_before>/**
 * Handles Project Organizer on dashboard page of OSF.
 * For Treebeard and _item API's check: https://github.com/caneruguz/treebeard/wiki
 */
'use strict';

var Treebeard = require('treebeard');

// CSS
require('css/typeahead.css');
require('css/fangorn.css');

var $ = require('jquery');
var m = require('mithril');
var moment = require('moment');
var $osf = require('js/osfHelpers');

var LinkObject;
var allProjectsCache;
var allTopLevelProjectsCache;
/* This sort projects is specific to data type used here for updates */
function sortProjects (flatList) {
    // Sorts by last modified
    flatList.sort(function(a,b){
        return new Date(b.data.attributes.date_modified) - new Date(a.data.attributes.date_modified);
    });
}

/**
 * Edits the template for the column titles.
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @this Treebeard.controller Check Treebeard API for methods available
 * @private
 */
function _poTitleColumn(item) {
    var tb = this;
    var preventSelect = function(e){
        e.stopImmediatePropagation();
    };
    var node = item.data; // Where actual data of the node is
    var css = ''; // Keep for future expandability -- Remove: item.data.isSmartFolder ? 'project-smart-folder smart-folder' : '';
    if (item.data.archiving) { // TODO check if this variable will be available
        return  m('span', {'class': 'registration-archiving'}, node.attributes.title + ' [Archiving]');
    } else if(node.links.html){
        return [ m('a.fg-file-links', { 'class' : css, href : node.links.html, 'data-nodeID' : node.id, onclick : function(event) {
            preventSelect.call(this, event);
            $osf.trackClick('myProjects', 'projectOrganizer', 'navigate-to-specific-project');
        }}, node.attributes.title) ];
    } else {
        return  m('span', { 'class' : css, 'data-nodeID' : node.id }, node.attributes.title);
    }
}

/**
 * Contributors have first person's name and then number of contributors. This function returns the proper html
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @returns {Object} A Mithril virtual DOM template object
 * @private
 */
function _poContributors(item) {
    var contributorList = item.data.embeds.contributors.data;
    if (contributorList.length === 0) {
        return '';
    }

    return contributorList.map(function (person, index, arr) {
        var name;
        var familyName = person.embeds.users.data.attributes.family_name;
        var givenName = person.embeds.users.data.attributes.given_name;
        if (familyName) {
            name = familyName;
        } else if(givenName){
            name = givenName;
        } else {
            name = 'A contributor';
        }
        var comma;
        if (index === 0) {
            comma = '';
        } else {
            comma = ', ';
        }
        if (index > 2) {
            return m('span');
        }
        if (index === 2) {
            return m('span', ' + ' + (arr.length - 2));
        }
        return m('span', comma + name);
    });
}

/**
 * Displays date modified
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @private
 */
function _poModified(item) {
    var node = item.data;
    var dateString = moment.utc(node.attributes.date_modified).fromNow();
    return m('span', dateString);
}

/**
 * Organizes all the row displays based on what that item requires.
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @returns {Array} An array of columns as col objects
 * @this Treebeard.controller Check Treebeard API For available methods
 * @private
 */
function _poResolveRows(item) {
    var mobile = window.innerWidth < 767; // true if mobile view
    var tb = this;
    var folderIcons = !tb.filterOn;
    var defaultColumns = [];

    if(this.isMultiselected(item.id)){
        item.css = 'fangorn-selected';
    } else {
        item.css = '';
    }

    defaultColumns.push({
        data : 'name',  // Data field name
        folderIcons : folderIcons,
        filter : true,
        css : 'po-draggable', // All projects are draggable since we separated collections from the grid
        custom : _poTitleColumn
    });

    if (!mobile) {
        defaultColumns.push({
            data : 'contributors',
            filter : true,
            custom : _poContributors
        }, {
            data : 'dateModified',
            filter : false,
            custom : _poModified
        });
    } else {
        defaultColumns.push({
            data : 'name',
            filter : false,
            custom : function (row){
                return m('.btn.btn-default.btn-sm[data-toggle="modal"][data-target="#infoModal"]', {
                }, m('i.fa.fa-ellipsis-h', {onclick: function() {
                    $osf.trackClick('myProjects', 'mobile', 'open-information-panel');
                }}));
            }
        });
    }

    return defaultColumns;
}

/**
 * Organizes the information for Column title row.
 * @returns {Array} An array of columns with pertinent column information
 * @private
 */
function _poColumnTitles() {
    var columns = [];
    var mobile = window.innerWidth < 767; // true if mobile view
    if(!mobile){
        columns.push({
            title: 'Name',
            width : '50%',
            sort : true,
            sortType : 'text'
        },{
            title : 'Contributors',
            width : '25%',
            sort : false
        }, {
            title : 'Modified',
            width : '25%',
            sort : false
        });
    } else {
        columns.push({
            title: 'Name',
            width : '90%',
            sort : true,
            sortType : 'text'
        },{
            title : '',
            width : '10%',
            sort : false
        });
    }

    return columns;
}

/**
 * Returns custom folder toggle icons for OSF
 * @param {Object} item A Treebeard _item object. Node information is inside item.data
 * @this Treebeard.controller
 * @returns {string} Returns a mithril template with m() function, or empty string.
 * @private
 */
function _poResolveToggle(item) {
    var toggleMinus = m('i.fa.fa-minus');
    var togglePlus = m('i.fa.fa-plus');
    var childrenCount = item.data.relationships.children ? item.data.relationships.children.links.related.meta.count : 0;
    if (childrenCount > 0) {
        if (item.open) {
            return toggleMinus;
        }
        return togglePlus;
    }
    return '';
}

/**
 * Resolves lazy load url for fetching children
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @this Treebeard.controller
 * @returns {String|Boolean} Returns the fetch URL in string or false if there is no url.
 * @private
 */
function _poResolveLazyLoad(item) {
    var node = item.data;
    if(item.children.length > 0) {
        return false;
    }
    if(node.relationships.children){
        //return node.relationships.children.links.related.href;
        var urlPrefix = node.attributes.registration ? 'registrations' : 'nodes';
        return $osf.apiV2Url(urlPrefix + '/' + node.id + '/children/', {
            query : {
                'related_counts' : 'children',
                'embed' : 'contributors'
            }
        });
    }
    return false;
}

/**
 * Hook to run after multiselect is run when an item is selected.
 * @param event Browser click event object
 * @param {Object} tree A Treebeard _item object. Node information is inside item.data
 * @this Treebeard.controller
 * @private
 */
function _poMultiselect(event, tree) {
    var tb = this;
    filterRowsNotInParent.call(tb, tb.multiselected());
    var scrollToItem = false;
    tb.options.updateSelected(tb.multiselected());
    if (tb.multiselected().length === 1) {
        tb.select('#tb-tbody').removeClass('unselectable');
        if (event.currentTarget != null) {
            $osf.trackClick('myProjects', 'projectOrganizer', 'single-project-selected');
        }
    } else if (tb.multiselected().length > 1) {
        $osf.trackClick('myProjects', 'projectOrganizer', 'multiple-projects-selected');
        tb.select('#tb-tbody').addClass('unselectable');
    }
    m.redraw();
}

/**
 * When multiple rows are selected remove those that are not in the parent
 * @param {Array} rows List of item objects
 * @returns {Array} newRows Returns the revised list of rows
 */
function filterRowsNotInParent(rows) {
    var tb = this;
    if (tb.multiselected().length < 2) {
        return tb.multiselected();
    }
    var i, newRows = [],
        originalRow = tb.find(tb.multiselected()[0].id),
        originalParent,
        currentItem;
    var changeColor = function() { $(this).css('background-color', ''); };
    if (typeof originalRow !== 'undefined') {
        originalParent = originalRow.parentID;
        for (i = 0; i < rows.length; i++) {
            currentItem = rows[i];
            if (currentItem.parentID === originalParent && currentItem.id !== -1) {
                newRows.push(rows[i]);
            } else {
                // The following row flashes a pastel red shade for a short time to denote that the row in question can't be multiselected with others
                $('.tb-row[data-id="' + rows[i].id + '"]').stop().css('background-color', '#D18C93').animate({ backgroundColor: '#fff'}, 500, changeColor);
            }
        }
    }
    tb.multiselected(newRows);
    tb.highlightMultiselect();
    return newRows;
}

/**
 * OSF-specific Treebeard options common to all addons.
 * For documentation visit: https://github.com/caneruguz/treebeard/wiki
 */
var tbOptions = {
    placement : 'dashboard',
    divID: 'projectOrganizer',
    rowHeight : 35,         // user can override or get from .tb-row height
    showTotal : 15,         // Actually this is calculated with div height, not needed. NEEDS CHECKING
    columnTitles : _poColumnTitles,
    resolveRows : _poResolveRows,
    showFilter : true,     // Gives the option to filter by showing the filter box.
    title : false,          // Title of the grid, boolean, string OR function that returns a string.
    allowMove : true,       // Turn moving on or off.
    moveClass : 'po-draggable',
    hoverClass : 'fangorn-hover',
    multiselect : true,
    hoverClassMultiselect : 'fangorn-selected',
    sortButtonSelector : {
        up : 'i.fa.fa-angle-up',
        down : 'i.fa.fa-angle-down'
    },
    sortDepth : 0,
    onload : function () {
        var tb = this,
            rowDiv = tb.select('.tb-row');
        rowDiv.first().trigger('click');
        $('.gridWrapper').on('mouseout', function () {
            tb.select('.tb-row').removeClass('po-hover');
        });
        m.render($(tb.options.dragContainment + ' .db-poFilter').get(0), tb.options.filterTemplate.call(this));
        tb.options.mpTreeData(tb.treeData);
        tb.options.mpBuildTree(tb.buildTree);
        tb.options.mpUpdateFolder(tb.updateFolder);
    },
    ontogglefolder : function (item, event) {
        $osf.trackClick('myProjects', 'projectOrganizer', 'expand-collapse-project-children');
        if (!item.open) {
            item.load = false;
        }
        $('[data-toggle="tooltip"]').tooltip();
    },
    onscrollcomplete : function () {
        $('[data-toggle="tooltip"]').tooltip();
    },
    onmultiselect : _poMultiselect,
    resolveIcon : function _poIconView(item) { // Project Organizer doesn't use icons
        return false;
    },
    resolveToggle : _poResolveToggle,
    resolveLazyloadUrl : _poResolveLazyLoad,
    resolveRefreshIcon : function () {
        return m('i.fa.fa-refresh.fa-spin');
    }, naturalScrollLimit : 0,
    removeIcon : function(){
        return m.trust('×');
    },
    headerTemplate : function(){ return ''; },
    xhrconfig : function(xhr) {
        xhr.withCredentials = true;
    },
    ondblclickrow : function(item, event){
        var tb = this;
        $osf.trackClick('myProjects', 'projectOrganizer', 'double-click-project');
        var node = item.data;
        var linkObject = new LinkObject('node', node, node.attributes.title);
        // Get ancestors
        linkObject.ancestors = [];
        function getAncestors (item) {
            var parent = item.parent();
            if(parent && parent.id > tb.treeData.id) {
                linkObject.ancestors.unshift(parent);
                getAncestors(parent);
            }
        }
        getAncestors(item);
        tb.options.updateFilesData(linkObject);
    },
    hScroll : 300,
    filterTemplate : function() {
        var tb = this;
        function resetFilter () {
            $osf.trackClick('myProjects', 'filter', 'clear-search');
            tb.filterText('');
            tb.resetFilter.call(tb);
            tb.updateFolder(allTopLevelProjectsCache(), tb.treeData);
            $('.db-poFilter>input').val('');
        }
        return [ m('input.form-control[placeholder="Search all my projects"][type="text"]', {
            style: 'display:inline;',
            onkeyup: function(event){
                if ($(this).val().length === 1){
                    tb.updateFolder(allProjectsCache(), tb.treeData);
                    tb.options.showSidebar(false);
                    tb.options.resetUi();
                }
                if($(this).val().length === 0){
                    resetFilter();
                }
                tb.filter(event);
            },
            onchange: function(event) {
                tb.filterText(event.value);
                $osf.trackClick('myProjects', 'filter', 'search-projects');
            },
            value: tb.filterText()
        }),
        m('.filterReset', { onclick : resetFilter }, tb.options.removeIcon())];
    },
    hiddenFilterRows : ['tags']
};

var ProjectOrganizer = {
    controller : function (args) {
        LinkObject = args.LinkObject;
        var self = this;
        self.updateTB = function(){
            var poOptions = $.extend(
                {
                    updateSelected : args.updateSelected,
                    updateFilesData : args.updateFilesData,
                    filesData: args.filesData(),
                    dragContainment : args.wrapperSelector,
                    resetUi : args.resetUi,
                    showSidebar : args.showSidebar,
                    loadValue : args.loadValue,
                    mpTreeData : args.treeData,
                    mpBuildTree : args.buildTree,
                    mpUpdateFolder : args.updateFolder
                },
                tbOptions
            );
            if (args.resolveToggle){
                poOptions.resolveToggle = args.resolveToggle;
            }
            var tb = new Treebeard(poOptions, true);
            return tb;
        };
        allProjectsCache = args.allProjects;
        allTopLevelProjectsCache = args.allTopLevelProjects;
        self.tb = self.updateTB();
    },
    view : function (ctrl, args) {
        return m('.fb-project-organizer#projectOrganizer', ctrl.tb );
    }
};

module.exports = ProjectOrganizer;

<commit_msg>Fix tb not catching up with search by using bounce and create a copy of the event object to not lose currentTarget attribute<commit_after>/**
 * Handles Project Organizer on dashboard page of OSF.
 * For Treebeard and _item API's check: https://github.com/caneruguz/treebeard/wiki
 */
'use strict';

var Treebeard = require('treebeard');

// CSS
require('css/typeahead.css');
require('css/fangorn.css');

var $ = require('jquery');
var m = require('mithril');
var moment = require('moment');
var $osf = require('js/osfHelpers');

var LinkObject;
var allProjectsCache;
var allTopLevelProjectsCache;
/* This sort projects is specific to data type used here for updates */
function sortProjects (flatList) {
    // Sorts by last modified
    flatList.sort(function(a,b){
        return new Date(b.data.attributes.date_modified) - new Date(a.data.attributes.date_modified);
    });
}

/**
 * Edits the template for the column titles.
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @this Treebeard.controller Check Treebeard API for methods available
 * @private
 */
function _poTitleColumn(item) {
    var tb = this;
    var preventSelect = function(e){
        e.stopImmediatePropagation();
    };
    var node = item.data; // Where actual data of the node is
    var css = ''; // Keep for future expandability -- Remove: item.data.isSmartFolder ? 'project-smart-folder smart-folder' : '';
    if (item.data.archiving) { // TODO check if this variable will be available
        return  m('span', {'class': 'registration-archiving'}, node.attributes.title + ' [Archiving]');
    } else if(node.links.html){
        return [ m('a.fg-file-links', { 'class' : css, href : node.links.html, 'data-nodeID' : node.id, onclick : function(event) {
            preventSelect.call(this, event);
            $osf.trackClick('myProjects', 'projectOrganizer', 'navigate-to-specific-project');
        }}, node.attributes.title) ];
    } else {
        return  m('span', { 'class' : css, 'data-nodeID' : node.id }, node.attributes.title);
    }
}

/**
 * Contributors have first person's name and then number of contributors. This function returns the proper html
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @returns {Object} A Mithril virtual DOM template object
 * @private
 */
function _poContributors(item) {
    var contributorList = item.data.embeds.contributors.data;
    if (contributorList.length === 0) {
        return '';
    }

    return contributorList.map(function (person, index, arr) {
        var name;
        var familyName = person.embeds.users.data.attributes.family_name;
        var givenName = person.embeds.users.data.attributes.given_name;
        if (familyName) {
            name = familyName;
        } else if(givenName){
            name = givenName;
        } else {
            name = 'A contributor';
        }
        var comma;
        if (index === 0) {
            comma = '';
        } else {
            comma = ', ';
        }
        if (index > 2) {
            return m('span');
        }
        if (index === 2) {
            return m('span', ' + ' + (arr.length - 2));
        }
        return m('span', comma + name);
    });
}

/**
 * Displays date modified
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @private
 */
function _poModified(item) {
    var node = item.data;
    var dateString = moment.utc(node.attributes.date_modified).fromNow();
    return m('span', dateString);
}

/**
 * Organizes all the row displays based on what that item requires.
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @returns {Array} An array of columns as col objects
 * @this Treebeard.controller Check Treebeard API For available methods
 * @private
 */
function _poResolveRows(item) {
    var mobile = window.innerWidth < 767; // true if mobile view
    var tb = this;
    var folderIcons = !tb.filterOn;
    var defaultColumns = [];

    if(this.isMultiselected(item.id)){
        item.css = 'fangorn-selected';
    } else {
        item.css = '';
    }

    defaultColumns.push({
        data : 'name',  // Data field name
        folderIcons : folderIcons,
        filter : true,
        css : 'po-draggable', // All projects are draggable since we separated collections from the grid
        custom : _poTitleColumn
    });

    if (!mobile) {
        defaultColumns.push({
            data : 'contributors',
            filter : true,
            custom : _poContributors
        }, {
            data : 'dateModified',
            filter : false,
            custom : _poModified
        });
    } else {
        defaultColumns.push({
            data : 'name',
            filter : false,
            custom : function (row){
                return m('.btn.btn-default.btn-sm[data-toggle="modal"][data-target="#infoModal"]', {
                }, m('i.fa.fa-ellipsis-h', {onclick: function() {
                    $osf.trackClick('myProjects', 'mobile', 'open-information-panel');
                }}));
            }
        });
    }

    return defaultColumns;
}

/**
 * Organizes the information for Column title row.
 * @returns {Array} An array of columns with pertinent column information
 * @private
 */
function _poColumnTitles() {
    var columns = [];
    var mobile = window.innerWidth < 767; // true if mobile view
    if(!mobile){
        columns.push({
            title: 'Name',
            width : '50%',
            sort : true,
            sortType : 'text'
        },{
            title : 'Contributors',
            width : '25%',
            sort : false
        }, {
            title : 'Modified',
            width : '25%',
            sort : false
        });
    } else {
        columns.push({
            title: 'Name',
            width : '90%',
            sort : true,
            sortType : 'text'
        },{
            title : '',
            width : '10%',
            sort : false
        });
    }

    return columns;
}

/**
 * Returns custom folder toggle icons for OSF
 * @param {Object} item A Treebeard _item object. Node information is inside item.data
 * @this Treebeard.controller
 * @returns {string} Returns a mithril template with m() function, or empty string.
 * @private
 */
function _poResolveToggle(item) {
    var toggleMinus = m('i.fa.fa-minus');
    var togglePlus = m('i.fa.fa-plus');
    var childrenCount = item.data.relationships.children ? item.data.relationships.children.links.related.meta.count : 0;
    if (childrenCount > 0) {
        if (item.open) {
            return toggleMinus;
        }
        return togglePlus;
    }
    return '';
}

/**
 * Resolves lazy load url for fetching children
 * @param {Object} item A Treebeard _item object for the row involved. Node information is inside item.data
 * @this Treebeard.controller
 * @returns {String|Boolean} Returns the fetch URL in string or false if there is no url.
 * @private
 */
function _poResolveLazyLoad(item) {
    var node = item.data;
    if(item.children.length > 0) {
        return false;
    }
    if(node.relationships.children){
        //return node.relationships.children.links.related.href;
        var urlPrefix = node.attributes.registration ? 'registrations' : 'nodes';
        return $osf.apiV2Url(urlPrefix + '/' + node.id + '/children/', {
            query : {
                'related_counts' : 'children',
                'embed' : 'contributors'
            }
        });
    }
    return false;
}

/**
 * Hook to run after multiselect is run when an item is selected.
 * @param event Browser click event object
 * @param {Object} tree A Treebeard _item object. Node information is inside item.data
 * @this Treebeard.controller
 * @private
 */
function _poMultiselect(event, tree) {
    var tb = this;
    filterRowsNotInParent.call(tb, tb.multiselected());
    var scrollToItem = false;
    tb.options.updateSelected(tb.multiselected());
    if (tb.multiselected().length === 1) {
        tb.select('#tb-tbody').removeClass('unselectable');
        if (event.currentTarget != null) {
            $osf.trackClick('myProjects', 'projectOrganizer', 'single-project-selected');
        }
    } else if (tb.multiselected().length > 1) {
        $osf.trackClick('myProjects', 'projectOrganizer', 'multiple-projects-selected');
        tb.select('#tb-tbody').addClass('unselectable');
    }
    m.redraw();
}

/**
 * When multiple rows are selected remove those that are not in the parent
 * @param {Array} rows List of item objects
 * @returns {Array} newRows Returns the revised list of rows
 */
function filterRowsNotInParent(rows) {
    var tb = this;
    if (tb.multiselected().length < 2) {
        return tb.multiselected();
    }
    var i, newRows = [],
        originalRow = tb.find(tb.multiselected()[0].id),
        originalParent,
        currentItem;
    var changeColor = function() { $(this).css('background-color', ''); };
    if (typeof originalRow !== 'undefined') {
        originalParent = originalRow.parentID;
        for (i = 0; i < rows.length; i++) {
            currentItem = rows[i];
            if (currentItem.parentID === originalParent && currentItem.id !== -1) {
                newRows.push(rows[i]);
            } else {
                // The following row flashes a pastel red shade for a short time to denote that the row in question can't be multiselected with others
                $('.tb-row[data-id="' + rows[i].id + '"]').stop().css('background-color', '#D18C93').animate({ backgroundColor: '#fff'}, 500, changeColor);
            }
        }
    }
    tb.multiselected(newRows);
    tb.highlightMultiselect();
    return newRows;
}

/**
 * OSF-specific Treebeard options common to all addons.
 * For documentation visit: https://github.com/caneruguz/treebeard/wiki
 */
var tbOptions = {
    placement : 'dashboard',
    divID: 'projectOrganizer',
    rowHeight : 35,         // user can override or get from .tb-row height
    showTotal : 15,         // Actually this is calculated with div height, not needed. NEEDS CHECKING
    columnTitles : _poColumnTitles,
    resolveRows : _poResolveRows,
    showFilter : true,     // Gives the option to filter by showing the filter box.
    title : false,          // Title of the grid, boolean, string OR function that returns a string.
    allowMove : true,       // Turn moving on or off.
    moveClass : 'po-draggable',
    hoverClass : 'fangorn-hover',
    multiselect : true,
    hoverClassMultiselect : 'fangorn-selected',
    sortButtonSelector : {
        up : 'i.fa.fa-angle-up',
        down : 'i.fa.fa-angle-down'
    },
    sortDepth : 0,
    onload : function () {
        var tb = this,
            rowDiv = tb.select('.tb-row');
        rowDiv.first().trigger('click');
        $('.gridWrapper').on('mouseout', function () {
            tb.select('.tb-row').removeClass('po-hover');
        });
        m.render($(tb.options.dragContainment + ' .db-poFilter').get(0), tb.options.filterTemplate.call(this));
        tb.options.mpTreeData(tb.treeData);
        tb.options.mpBuildTree(tb.buildTree);
        tb.options.mpUpdateFolder(tb.updateFolder);
    },
    ontogglefolder : function (item, event) {
        $osf.trackClick('myProjects', 'projectOrganizer', 'expand-collapse-project-children');
        if (!item.open) {
            item.load = false;
        }
        $('[data-toggle="tooltip"]').tooltip();
    },
    onscrollcomplete : function () {
        $('[data-toggle="tooltip"]').tooltip();
    },
    onmultiselect : _poMultiselect,
    resolveIcon : function _poIconView(item) { // Project Organizer doesn't use icons
        return false;
    },
    resolveToggle : _poResolveToggle,
    resolveLazyloadUrl : _poResolveLazyLoad,
    resolveRefreshIcon : function () {
        return m('i.fa.fa-refresh.fa-spin');
    }, naturalScrollLimit : 0,
    removeIcon : function(){
        return m.trust('×');
    },
    headerTemplate : function(){ return ''; },
    xhrconfig : function(xhr) {
        xhr.withCredentials = true;
    },
    ondblclickrow : function(item, event){
        var tb = this;
        $osf.trackClick('myProjects', 'projectOrganizer', 'double-click-project');
        var node = item.data;
        var linkObject = new LinkObject('node', node, node.attributes.title);
        // Get ancestors
        linkObject.ancestors = [];
        function getAncestors (item) {
            var parent = item.parent();
            if(parent && parent.id > tb.treeData.id) {
                linkObject.ancestors.unshift(parent);
                getAncestors(parent);
            }
        }
        getAncestors(item);
        tb.options.updateFilesData(linkObject);
    },
    hScroll : 300,
    filterTemplate : function() {
        var tb = this;
        function resetFilter () {
            $osf.trackClick('myProjects', 'filter', 'clear-search');
            tb.filterText('');
            tb.resetFilter.call(tb);
            tb.updateFolder(allTopLevelProjectsCache(), tb.treeData);
            $('.db-poFilter>input').val('');
        }
        var filter = $osf.debounce(tb.filter, 800);
        return [ m('input.form-control[placeholder="Search all my projects"][type="text"]', {
            style: 'display:inline;',
            onkeyup: function(event){
                var newEvent = $.extend({}, event); // because currentTarget changes with debounce.
                if ($(this).val().length === 1){
                    tb.updateFolder(allProjectsCache(), tb.treeData);
                    tb.options.showSidebar(false);
                    tb.options.resetUi();
                }
                if($(this).val().length === 0){
                    resetFilter();
                }
                if($(this).val().length > 1){
                    filter(newEvent);
                }
            },
            onchange: function(event) {
                tb.filterText(event.value);
                $osf.trackClick('myProjects', 'filter', 'search-projects');
            },
            value: tb.filterText()
        }),
        m('.filterReset', { onclick : resetFilter }, tb.options.removeIcon())];
    },
    hiddenFilterRows : ['tags']
};

var ProjectOrganizer = {
    controller : function (args) {
        LinkObject = args.LinkObject;
        var self = this;
        self.updateTB = function(){
            var poOptions = $.extend(
                {
                    updateSelected : args.updateSelected,
                    updateFilesData : args.updateFilesData,
                    filesData: args.filesData(),
                    dragContainment : args.wrapperSelector,
                    resetUi : args.resetUi,
                    showSidebar : args.showSidebar,
                    loadValue : args.loadValue,
                    mpTreeData : args.treeData,
                    mpBuildTree : args.buildTree,
                    mpUpdateFolder : args.updateFolder
                },
                tbOptions
            );
            if (args.resolveToggle){
                poOptions.resolveToggle = args.resolveToggle;
            }
            var tb = new Treebeard(poOptions, true);
            return tb;
        };
        allProjectsCache = args.allProjects;
        allTopLevelProjectsCache = args.allTopLevelProjects;
        self.tb = self.updateTB();
    },
    view : function (ctrl, args) {
        return m('.fb-project-organizer#projectOrganizer', ctrl.tb );
    }
};

module.exports = ProjectOrganizer;

<|endoftext|>


<commit_before>"use strict";

var settings = require("../settings"),
    cordovaTasks = require("../utils/cordova-tasks");
/**
 * Builds the cordova portion of the project
 */
function buildCordova() {
    if (settings.NO_BUILD) {
        return;
    }
    var target = settings.TARGET_DEVICE;
    if (!target || target === "" || target === "--target=device") {
        target = "--device";
    }
    return cordovaTasks.build({buildMode: settings.BUILD_MODE, platforms: settings.BUILD_PLATFORMS, options: [target, "--buildConfig=build.json"]});
}

module.exports = buildCordova;
<commit_msg>Do an actual cordova prepare, duh!<commit_after>"use strict";

var settings = require("../settings"),
    cordovaTasks = require("../utils/cordova-tasks");
/**
 * Builds the cordova portion of the project
 */
function prepareCordova() {
    return cordovaTasks.prepare();
}

module.exports = prepareCordova;
<|endoftext|>


<commit_before>/*
 * File: jetpack/FDEditor.Ace.js
 * Extends functionality of FDEditor to support Ace API
 *
 * Class: FDEditor
 */
var Class = require('shipyard/class/Class'),
    object = require('shipyard/utils/object'),
    log = require('shipyard/utils/log'),
    ace = require('ace/ace'),
    FDEditor = require('./FDEditor');

var JSMode = require('ace/modes/javascript').Mode,
    CSSMode = require('ace/modes/css').Mode,
    HTMLMode = require('ace/modes/html').Mode,
    TextMode = require('ace/modes/txt').Mode;

var Modes = {
    'javascript': new JSMode(),
    'css': new CSSMode(),
    'html': new HTMLMode(),
    'txt': new TextMode()
};

var stripDeltas = function(stack) {
    // Change Stack (undo/redo) in Ace is represented as
    // [[DeltaObject1], [DeltaObject2], ... ]
    // these objects have to be copied - not referenced
    var deltas = [];
    stack.each(function(single){
        deltas.push(object.merge({}, single[0]));
    });
    return deltas;
};

var buildStack = function(deltas) {
    // Extract stored deltas into the stack
    var stack = [];
    deltas.each(function(delta){
        stack.push([object.merge({}, delta)]);
    });
    return stack;
};

module.exports = new Class({

    Extends: FDEditor,

    options: {
        element_type: 'div'
    },

    modes: {},

    available_modes: ['js', 'txt', 'html', 'css'],

    default_kind: 'txt',

    mode_translate: {
        'js': 'javascript',
        'txt': 'text'
    },

    initialize: function(wrapper, options) {
        this.parent(wrapper, options);
        this.editor = ace.edit(this.element.getNode());
        this.editor.getSession().setUseWorker(false);
        this.spinner = this.element.getElement('.ace_scroller');

        var that = this;

        ['blur', 'focus'].each(function(ev) {
            that.editor.on(ev, function(){
                that.emit(ev);
            });
        });
    },

    setEditable: function() {
        this.editor.setReadOnly(false);
        this.hookChangeIfNeeded();
    },

    setReadOnly: function() {
        this.editor.setReadOnly(true);
        if (this.change_hooked) {
            this.unhookChange();
        }
    },

    hookChange: function(){
        // hook to onChange Event
        this.editor.getSession().on('change', this.boundWhenItemChanged);
        this.change_hooked = true;
    },

    unhookChange: function(){
        // unhook the onChange Event
        this.editor.getSession().removeEventListener('change', this.boundWhenItemChanged);
        this.change_hooked = false;
    },

    getContent: function(){
        var value = this.editor.getSession().getValue();
        return value;
    },

    _setContent: function(value){
        this.editor.getSession().setValue(value);
        var cursor = this.current.cursorPos || { row: 0, column: 0 };
        this.editor.selection.moveCursorTo(cursor.row, cursor.column);
    },

    getUndoRedoStack: function() {
        var current_manager = this.editor.getSession().getUndoManager();
        return {
            redoDeltas: stripDeltas(current_manager.$redoStack),
            undoDeltas: stripDeltas(current_manager.$undoStack)
        };
    },
    setUndoRedoStack: function(obj){
        var current_manager = this.editor.getSession().getUndoManager();
        current_manager.$redoStack = buildStack(obj.redoDeltas);
        current_manager.$undoStack = buildStack(obj.undoDeltas);
    },

    dumpCurrent: function() {
        // dump undo buffers
        this.current.undo_manager = this.getUndoRedoStack();
        this.current.cursorPos = this.editor.selection.getCursor();
        this.parent();
    },

    activateItem: function(item) {
        this.parent(item);
        // load undo buffers
        if (this.current.undo_manager) {
            this.setUndoRedoStack(this.current.undo_manager);
        }
    },

    setSyntax: function(kind){
        if (this.available_modes.indexOf(kind) === -1) {
            log.info('Mode (%s) not found. Using txt mode.', kind);
            kind = this.default_kind;
        } else if (this.mode_translate[kind]) {
            kind = this.mode_translate[kind];
        }

        if (!Modes[kind]) {
            // This should only ever be the case if above is broken.
            // ie, available_modes and mode_translate aren't in sync
            // with Modes at the top.
            log.warn('Mode (%s) not found. Bad.');
            kind = this.default_kind;
        }
        this.editor.getSession().setMode(Modes[kind]);
    }

});
<commit_msg>wrong names for Ace Modes<commit_after>/*
 * File: jetpack/FDEditor.Ace.js
 * Extends functionality of FDEditor to support Ace API
 *
 * Class: FDEditor
 */
var Class = require('shipyard/class/Class'),
    object = require('shipyard/utils/object'),
    log = require('shipyard/utils/log'),
    ace = require('ace/ace'),
    FDEditor = require('./FDEditor');

var JSMode = require('ace/mode/javascript').Mode,
    CSSMode = require('ace/mode/css').Mode,
    HTMLMode = require('ace/mode/html').Mode,
    TextMode = require('ace/mode/text').Mode;

var Modes = {
    'javascript': new JSMode(),
    'css': new CSSMode(),
    'html': new HTMLMode(),
    'txt': new TextMode()
};

var stripDeltas = function(stack) {
    // Change Stack (undo/redo) in Ace is represented as
    // [[DeltaObject1], [DeltaObject2], ... ]
    // these objects have to be copied - not referenced
    var deltas = [];
    stack.each(function(single){
        deltas.push(object.merge({}, single[0]));
    });
    return deltas;
};

var buildStack = function(deltas) {
    // Extract stored deltas into the stack
    var stack = [];
    deltas.each(function(delta){
        stack.push([object.merge({}, delta)]);
    });
    return stack;
};

module.exports = new Class({

    Extends: FDEditor,

    options: {
        element_type: 'div'
    },

    modes: {},

    available_modes: ['js', 'txt', 'html', 'css'],

    default_kind: 'txt',

    mode_translate: {
        'js': 'javascript',
        'txt': 'text'
    },

    initialize: function(wrapper, options) {
        this.parent(wrapper, options);
        this.editor = ace.edit(this.element.getNode());
        this.editor.getSession().setUseWorker(false);
        this.spinner = this.element.getElement('.ace_scroller');

        var that = this;

        ['blur', 'focus'].each(function(ev) {
            that.editor.on(ev, function(){
                that.emit(ev);
            });
        });
    },

    setEditable: function() {
        this.editor.setReadOnly(false);
        this.hookChangeIfNeeded();
    },

    setReadOnly: function() {
        this.editor.setReadOnly(true);
        if (this.change_hooked) {
            this.unhookChange();
        }
    },

    hookChange: function(){
        // hook to onChange Event
        this.editor.getSession().on('change', this.boundWhenItemChanged);
        this.change_hooked = true;
    },

    unhookChange: function(){
        // unhook the onChange Event
        this.editor.getSession().removeEventListener('change', this.boundWhenItemChanged);
        this.change_hooked = false;
    },

    getContent: function(){
        var value = this.editor.getSession().getValue();
        return value;
    },

    _setContent: function(value){
        this.editor.getSession().setValue(value);
        var cursor = this.current.cursorPos || { row: 0, column: 0 };
        this.editor.selection.moveCursorTo(cursor.row, cursor.column);
    },

    getUndoRedoStack: function() {
        var current_manager = this.editor.getSession().getUndoManager();
        return {
            redoDeltas: stripDeltas(current_manager.$redoStack),
            undoDeltas: stripDeltas(current_manager.$undoStack)
        };
    },
    setUndoRedoStack: function(obj){
        var current_manager = this.editor.getSession().getUndoManager();
        current_manager.$redoStack = buildStack(obj.redoDeltas);
        current_manager.$undoStack = buildStack(obj.undoDeltas);
    },

    dumpCurrent: function() {
        // dump undo buffers
        this.current.undo_manager = this.getUndoRedoStack();
        this.current.cursorPos = this.editor.selection.getCursor();
        this.parent();
    },

    activateItem: function(item) {
        this.parent(item);
        // load undo buffers
        if (this.current.undo_manager) {
            this.setUndoRedoStack(this.current.undo_manager);
        }
    },

    setSyntax: function(kind){
        if (this.available_modes.indexOf(kind) === -1) {
            log.info('Mode (%s) not found. Using txt mode.', kind);
            kind = this.default_kind;
        } else if (this.mode_translate[kind]) {
            kind = this.mode_translate[kind];
        }

        if (!Modes[kind]) {
            // This should only ever be the case if above is broken.
            // ie, available_modes and mode_translate aren't in sync
            // with Modes at the top.
            log.warn('Mode (%s) not found. Bad.');
            kind = this.default_kind;
        }
        this.editor.getSession().setMode(Modes[kind]);
    }

});
<|endoftext|>


<commit_before>(function () {
    "use strict";

    angular
        .module('orange')
        .factory('medications', medications);

    medications.$inject = ['$rootScope', '$q', 'n2w', 'notifications'];

    /* @ngInject */
    function medications($rootScope, $q, n2w, notify) {
        var vm = this;
        var service = {
            setMedicationSchedule: setMedicationSchedule,
            setMedicationEvents: setMedicationEvents,
            saveMedication: saveMedication,
            setNotifications: setNotifications,
            getMedication: getMedication,
            setMedication: setMedication,
            get: get,
            fetch: fetch,
            getAll: getAll,
            fetchAll: fetchAll,
            getMedications: getMedications,
            remove: remove,
            setLog: setLog,
            getEventText: getEventText,

            createMedication: createMedication
        };

        vm.medications = null;
        vm.medication = null;
        vm.log = null;

        $rootScope.$on('auth:user:logout', clean);

        return service;

        ////////////////

        function clean() {
            console.log('User logged out, cleaning medications service data...');
            vm.medications = null;
            vm.medication = null;
            vm.log = null;
        }

        function saveMedication(medication) {
            medication = medication || vm.medication;
            if (medication.id) {
                return medication.save().then(
                    function(medication) {
                        vm.medication = medication;
                        notify.updateNotify();
                        return medication;
                    },
                    function(error) {
                        return error;
                    }
                )
            } else {
                return vm.log.all('medications').post(medication).then(
                    function (medication) {
                        vm.medication = medication;
                        if (!(medication.import_id && _.find(vm.medications, {import_id: medication.import_id}))) {
                            vm.medications.push(medication);
                        }
                        notify.addNotifyByMedication(medication);
                        return medication;
                    },
                    function (error) {
                        return error;
                    }
                )
            }
        }

        function setNotifications(notifications) {
            var promises = [];
            for (var i = 0, len = vm.medication.schedule.times.length; i < len; i++) {
                var time = vm.medication.schedule.times[i];
                var notification = notifications[i] || 30;
                console.log('setting notification time', vm.medication.id, time.id, notification);
                var promise = vm.medication.all('times').one(time.id.toString()).customPUT({
                   user: notification
                });
                promises.push(promise);
            }
            return $q.all(promises);
        }

        function setMedication(medication) {
            vm.medication = medication;
        }

        function setMedicationSchedule(schedule) {
            vm.medication.schedule = schedule;
        }

        function setMedicationEvents(events) {
            vm.medication.schedule.times = events;
        }

        function getMedication() {
            return vm.medication;
        }

        function get(id) {
            id = parseInt(id);
            var deffered = $q.defer();
            var promise = deffered.promise;
            if (vm.medication && !id) {
                deffered.resolve(vm.medication);
            } else if (vm.medication && vm.medication.id === id) {
                deffered.resolve(vm.medication);
            } else if (vm.medications !== null) {
                vm.medication = _.find(vm.medications, {'id': id});
                deffered.resolve(vm.medication);
            } else {
                promise = fetch(id);
            }

            return promise;
        }

        function fetch(id) {
            var deffered = $q.defer();
            vm.log.all('medications').get(id).then(
                function (medication) {
                    vm.medication = medication;
                    deffered.resolve(medication);
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function getMedications() {
            return vm.medications;
        }

        function setLog(log) {
            vm.log = log;
            vm.medications = null;
            vm.medication = null;

        }

        function getAll() {
            var deffered = $q.defer();
            if (vm.medications !== null) {
                deffered.resolve(vm.medications);
                return deffered.promise;
            } else {
                return fetchAll();
            }
        }

        function remove(medication) {
            var deffered = $q.defer();

            medication.remove().then(
                function () {
                    if (vm.medications !== null) {
                        vm.medications = _.without(vm.medications, medication);
                    }
                    vm.medication = null;
                    deffered.resolve();

                    notify.updateNotify();
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function fetchAll() {
            var deffered = $q.defer();

            vm.log.all('medications').getList().then(
                function (medications) {
                    vm.medications = medications;
                    vm.medication = null;
                    deffered.resolve(medications);
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function getEventText(event) {
            var result = '';
            var units = vm.medication.dose.quantity;
            result += _.capitalize(n2w.toWords(units || 0));
            result += ' unit' + (units === 1 ? '' : 's');
            if (event.type == 'exact') {
                result += ' at ' + event.time;
            } else {
                result += ' ' + event.when + ' ' + event.event;
            }
            return result;
        }

        function createMedication(medication) {
            return vm.log.all('medications').post(medication).then(
                function (medication) {
                    vm.medications.push(medication);
                    return medication;
                });
        }
    }
})();
<commit_msg>[bugfix] Fixed medications service<commit_after>(function () {
    "use strict";

    angular
        .module('orange')
        .factory('medications', medications);

    medications.$inject = ['$rootScope', '$q', 'n2w', 'notifications'];

    /* @ngInject */
    function medications($rootScope, $q, n2w, notify) {
        var vm = this;
        var service = {
            setMedicationSchedule: setMedicationSchedule,
            setMedicationEvents: setMedicationEvents,
            saveMedication: saveMedication,
            setNotifications: setNotifications,
            getMedication: getMedication,
            setMedication: setMedication,
            get: get,
            fetch: fetch,
            getAll: getAll,
            fetchAll: fetchAll,
            getMedications: getMedications,
            remove: remove,
            setLog: setLog,
            getEventText: getEventText,

            createMedication: createMedication
        };

        vm.medications = null;
        vm.medication = null;
        vm.log = null;

        $rootScope.$on('auth:user:logout', clean);

        return service;

        ////////////////

        function clean() {
            console.log('User logged out, cleaning medications service data...');
            vm.medications = null;
            vm.medication = null;
            vm.log = null;
        }

        function saveMedication(medication) {
            medication = medication || vm.medication;
            if (medication.id) {
                return medication.save().then(
                    function(medication) {
                        vm.medication = medication;
                        notify.updateNotify();
                        return medication;
                    },
                    function(error) {
                        return error;
                    }
                )
            } else {
                return vm.log.all('medications').post(medication).then(
                    function (medication) {
                        vm.medication = medication;
                        if (!(medication.import_id && _.find(vm.medications, {import_id: medication.import_id}))) {
                            vm.medications.push(medication);
                        }
                        notify.addNotifyByMedication(medication);
                        return medication;
                    },
                    function (error) {
                        return error;
                    }
                )
            }
        }

        function setNotifications(notifications) {
            var promises = [];
            for (var i = 0, len = vm.medication.schedule.times.length; i < len; i++) {
                var time = vm.medication.schedule.times[i];
                var notification = notifications[i] || 30;
                console.log('setting notification time', vm.medication.id, time.id, notification);
                var promise = vm.medication.all('times').one(time.id.toString()).customPUT({
                   user: notification
                });
                promises.push(promise);
            }
            return $q.all(promises);
        }

        function setMedication(medication) {
            vm.medication = medication;
        }

        function setMedicationSchedule(schedule) {
            vm.medication.schedule = schedule;
        }

        function setMedicationEvents(events) {
            vm.medication.schedule.times = events;
        }

        function getMedication() {
            return vm.medication;
        }

        function get(id) {
            id = parseInt(id);
            var deffered = $q.defer();
            var promise = deffered.promise;
            if (vm.medication && !id) {
                deffered.resolve(vm.medication);
            } else if (vm.medication && vm.medication.id === id) {
                deffered.resolve(vm.medication);
            } else if (vm.medications !== null) {
                vm.medication = _.find(vm.medications, {'id': id});
                deffered.resolve(vm.medication);
            } else {
                promise = fetch(id);
            }

            return promise;
        }

        function fetch(id) {
            var deffered = $q.defer();
            vm.log.all('medications').get(id).then(
                function (medication) {
                    vm.medication = medication;
                    deffered.resolve(medication);
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function getMedications() {
            return vm.medications;
        }

        function setLog(log) {
            if (vm.log && log.id !== vm.log.id) {
                vm.medications = null;
                vm.medication = null;
            }
            vm.log = log;
        }

        function getAll() {
            var deffered = $q.defer();
            if (vm.medications !== null) {
                deffered.resolve(vm.medications);
                return deffered.promise;
            } else {
                return fetchAll();
            }
        }

        function remove(medication) {
            var deffered = $q.defer();

            medication.remove().then(
                function () {
                    if (vm.medications !== null) {
                        vm.medications = _.without(vm.medications, medication);
                    }
                    vm.medication = null;
                    deffered.resolve();

                    notify.updateNotify();
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function fetchAll() {
            var deffered = $q.defer();

            vm.log.all('medications').getList().then(
                function (medications) {
                    vm.medications = medications;
                    vm.medication = null;
                    deffered.resolve(medications);
                },
                function (error) {
                    deffered.reject(error);
                }
            );
            return deffered.promise;
        }

        function getEventText(event) {
            var result = '';
            var units = vm.medication.dose.quantity;
            result += _.capitalize(n2w.toWords(units || 0));
            result += ' unit' + (units === 1 ? '' : 's');
            if (event.type == 'exact') {
                result += ' at ' + event.time;
            } else {
                result += ' ' + event.when + ' ' + event.event;
            }
            return result;
        }

        function createMedication(medication) {
            return vm.log.all('medications').post(medication).then(
                function (medication) {
                    vm.medications.push(medication);
                    return medication;
                });
        }
    }
})();
<|endoftext|>


<commit_before>var Promise = require("bluebird");
var yargs = require('yargs');
var _ = require('lodash');
var Table = require('cli-table2');
var colors = require('colors');

var bittrex = require('node.bittrex.api');

bittrex.options({
    apikey: '',
    apisecret: ''
});

var request = Promise.promisify(require("request"));

module.exports = balanceCommand;

function getBitcoinUSDPrice(){
    return request({url:'http://api.coindesk.com/v1/bpi/currentprice.json', json: true}).then(function(response){
        var body = response.body;
        return body.bpi.USD.rate_float
    });
}

function getBalances(){
    return new Promise(function(resolve, reject){
        bittrex.getbalances(function(response){
            if(response.error){
                reject(response.error);
            } else {
                resolve(response.result);
            }
        });
    });
}

function getMarketSummaries(){
    return new Promise(function(resolve, reject){
        bittrex.getmarketsummaries(function(data){
            if(data.error){
                reject(data.error);
            } else {
                resolve(data.result);
            }
        });
    });
}

function getTicker(){
    return new Promise(function(resolve, reject){
        bittrex.getticker(function(response){
            if(response.error){
                reject(response.error);
            } else {
                resolve(response.result);
            }
        });
    });
}

function balanceCommand(argv){
    //getBalances().then(console.log)
    // getMarketSummaries().then(function(asdf){
    //     var zzz = asdf.map(function(foo){
    //         return foo.MarketName
    //     })
    //     console.log(zzz.join(' '))
    // })
    Promise.all([getBalances(), getMarketSummaries(), getBitcoinUSDPrice()]).then(function(data){
        var balances = data[0];
        var marketSummaries = data[1];
        var bitcoinPriceUSD = data[2];

        var coinBalances = balances.filter(function(balance){
            return balance.Balance > 0;
        });

        var balancesObj = {};
        coinBalances.forEach(function(balance){
            balancesObj[balance.Currency] = balance;
        });

        //console.log(balancesObj);

        var marketsObj = {};

        marketSummaries.filter(function(market){
            return /BTC\-\w+/.test(market.MarketName);
        }).map(function(market){
            var uuu = market.MarketName.split('-');
            var last = market.Last;
            var prev = market.PrevDay;
            var percentChange = (last - prev)/prev*100.0
            return {
                price: market.Last,
                percentChange: percentChange,
                baseCurrency: uuu[0],
                tradeCurrency: uuu[1]
            };
        }).forEach(function(market){
            marketsObj[market.tradeCurrency] = market;
        });

        //console.log(marketsObj);

        for(var key in balancesObj){
            var newObj = Object.assign(balancesObj[key], marketsObj[key]);
            if(newObj.Currency === 'BTC'){
                newObj.price = bitcoinPriceUSD;
                newObj.percentChange = 0;
                newObj.baseCurrency = 'USD';
                newObj.tradeCurrency = 'BTC';
            }
            balancesObj[key] = newObj;
        }

        var table  = new Table({head: ['Coin'.cyan, 'Rate'.cyan, '% Change'.cyan, 'BTC Value'.cyan, 'USD Value'.cyan]});

        var tableRows = [];
        for(var key in balancesObj){
            var market = balancesObj[key];

            var pc = market.percentChange;
            var pc2 = '';
            if(pc > 0){
                pc2 = pc.toFixed(2).green;
            } else {
                pc2 = pc.toFixed(2).red;
            }
            var btcValue = market.Balance * market.price

            tableRows.push([market.baseCurrency+'-'+market.tradeCurrency, market.price.toFixed(8), pc2, btcValue.toFixed(8), '$'+((btcValue * bitcoinPriceUSD).toFixed(2))]);
        }

        _.orderBy(tableRows,function(row){
            return row[3];
        },'desc').forEach(function(row){
            table.push(row);
        });

        //print the table
        console.log(table.toString());
    })

}<commit_msg>balance command working<commit_after>var Promise = require("bluebird");
var yargs = require('yargs');
var _ = require('lodash');
var Table = require('cli-table2');
var colors = require('colors');

var bittrex = require('node.bittrex.api');
var apiKey = process.env.BITTREX_API_KEY;
var secret = process.env.BITTREX_API_SECRET;

bittrex.options({
    apikey: apiKey,
    apisecret: secret
});

var request = Promise.promisify(require("request"));

module.exports = balanceCommand;

function getBitcoinUSDPrice(){
    return request({url:'http://api.coindesk.com/v1/bpi/currentprice.json', json: true}).then(function(response){
        var body = response.body;
        return body.bpi.USD.rate_float
    });
}

function getBalances(){
    return new Promise(function(resolve, reject){
        bittrex.getbalances(function(response){
            if(response.error){
                reject(response.error);
            } else {
                resolve(response.result);
            }
        });
    });
}

function getMarketSummaries(){
    return new Promise(function(resolve, reject){
        bittrex.getmarketsummaries(function(data){
            if(data.error){
                reject(data.error);
            } else {
                resolve(data.result);
            }
        });
    });
}

function getTicker(){
    return new Promise(function(resolve, reject){
        bittrex.getticker(function(response){
            if(response.error){
                reject(response.error);
            } else {
                resolve(response.result);
            }
        });
    });
}

function balanceCommand(argv){
    //getBalances().then(console.log)
    // getMarketSummaries().then(function(asdf){
    //     var zzz = asdf.map(function(foo){
    //         return foo.MarketName
    //     })
    //     console.log(zzz.join(' '))
    // })
    Promise.all([getBalances(), getMarketSummaries(), getBitcoinUSDPrice()]).then(function(data){
        var balances = data[0];
        var marketSummaries = data[1];
        var bitcoinPriceUSD = data[2];

        var coinBalances = balances.filter(function(balance){
            return balance.Balance > 0;
        });

        var balancesObj = {};
        coinBalances.forEach(function(balance){
            balancesObj[balance.Currency] = balance;
        });

        //console.log(balancesObj);

        var marketsObj = {};

        marketSummaries.filter(function(market){
            return /BTC\-\w+/.test(market.MarketName);
        }).map(function(market){
            var uuu = market.MarketName.split('-');
            var last = market.Last;
            var prev = market.PrevDay;
            var percentChange = (last - prev)/prev*100.0
            return {
                price: market.Last,
                percentChange: percentChange,
                baseCurrency: uuu[0],
                tradeCurrency: uuu[1]
            };
        }).forEach(function(market){
            marketsObj[market.tradeCurrency] = market;
        });

        //console.log(marketsObj);

        for(var key in balancesObj){
            var newObj = Object.assign(balancesObj[key], marketsObj[key]);
            if(newObj.Currency === 'BTC'){
                newObj.price = bitcoinPriceUSD;
                newObj.percentChange = 0;
                newObj.baseCurrency = 'USD';
                newObj.tradeCurrency = 'BTC';
            }
            balancesObj[key] = newObj;
        }

        var table  = new Table({head: ['Coin'.cyan, 'Rate'.cyan, '% Change'.cyan, 'BTC Value'.cyan, 'USD Value'.cyan]});

        var tableRows = [];
        for(var key in balancesObj){
            var market = balancesObj[key];

            var pc = market.percentChange;
            var pc2 = '';
            if(pc > 0){
                pc2 = pc.toFixed(2).green;
            } else {
                pc2 = pc.toFixed(2).red;
            }
            var btcValue = market.Balance * market.price

            tableRows.push([market.baseCurrency+'-'+market.tradeCurrency, market.price.toFixed(8), pc2, btcValue.toFixed(8), '$'+((btcValue * bitcoinPriceUSD).toFixed(2))]);
        }

        _.orderBy(tableRows,function(row){
            return row[3];
        },'desc').forEach(function(row){
            table.push(row);
        });

        //print the table
        console.log(table.toString());
    })

}<|endoftext|>