<commit_before>function Bitcoin(app, host, port, user, pass) {
	this.app = app;

	this.RPCHost = host;
	this.RPCPort = port;
	this.RPCUser = user;
	this.RPCPass = pass;
	this.RPCURL;

	this.prepareURL = function() {
		var url = "http://";

		if(this.RPCUser) {
			url += this.RPCUser;
			if(this.RPCPass) {
				url += ":" + this.RPCPass;
			}
			url += "@";
		}

		url += this.RPCHost;

		if(this.RPCPort) {
			url += ":" + this.RPCPort;
		}

		url += "/?callback=?";

		return url;
	}

	this.RPC = function(method, params, callback) {
		var request;

		if(params != null) {
			for(var key in params) {
				params[key] = params[key];
			}
			request = {method: method, params: params};
		} else {
			request = {method: method};
		}

		jQuery.getJSON(this.RPCURL, request, function(data) {
					callback(data.result, data.error);
				});
	}

	this.listTransactions = function(account) {
		this.RPC("listtransactions", ['"' + account + '"', 999999], this.app.onListTransactions);
	}

	this.validateAddress = function(callback, address) {
		this.RPC("validateaddress", ['"' + address + '"'], callback);
	}

	this.sendBTC = function(account, address, amount) {
		this.RPC("sendfrom", ['"' + account + '"', address, amount], this.app.onSendBTC);
	}

	this.getAddress = function(account) {
		this.RPC("getaccountaddress", ['"' + account + '"'], this.app.onGetAddress);
	}

	this.getBalance = function(account) {
		this.RPC("getbalance", ['"' + account + '"'], this.app.onGetBalance);
	}

	this.getInfo = function() {
		this.RPC("getinfo", null, this.app.onGetInfo);
	}

	this.connect = function() {
		this.RPC("getinfo", null, this.app.onConnect);
	}

	this.init = function() {
		this.RPCURL = this.prepareURL();
	}
}

function setFormValue(form, name, value) {
	var obj = $(form).children('input[name="' + name + '"]');
	obj.val(value);

	return obj;
}

function getFormValue(form, name) {
	return $(form).children('input[name="' + name + '"]').val();
}

function sortTransactions(a, b) {
	if(b.time != a.time)
		return (b.time - a.time);

	if(b.category != a.category)
		return (a.category < b.category) ? -1 : 1;

	return (a.amount - b.amount);
}

function hideValidation(obj) {
	var o = $(obj).next('span').removeClass().text('');
}

function showValidation(obj, correct) {
	var o = $(obj).next('span');

	if(correct) {
		o.removeClass().addClass('valRight').html("✔");
	} else {
		o.removeClass().addClass('valWrong').html("✘");
	}
}

/* Because of the way JSONP works this codes assumes a global
 * variable named 'app' pointing to the BitcoinApp() instance!
 *
 *
 */

function BitcoinApp() {
	this.bitcoin = false;
	this.account = "";
	this.balance;
	this.connected = false;
	this.refreshTimeout = false;
	this.dateFormat = "dd/mm/yyyy HH:MM";

	this.onGetBalance = function(balance) {
		$('#balance').text(balance.formatBTC());
		app.balance = balance;
	}

	this.onGetAddress = function(address) {
		var addressField = $('#address');
		if(addressField.text() != address)
			$('#address').text(address);
	}

	this.onConnect = function(info) {
		if(info.version) {
			app.connected = true;

			app.refreshAccount();

			$('#section_Settings').next().slideUp('fast');
			$('#accountInfo').slideDown('fast');
			$('#section_SendBTC').show();
			$('#section_TX').show().next().show();
			$('#serverInfo').show();
		}
	}

	this.onDisconnect = function() {
		app.connected = false;
		app.balance = false;

		$('#title').text("Bitcoin (not connected)");
		$('#accountInfo').slideUp('fast');
		$('#serverInfo').hide();
		$('#serverInfo table').children().remove();
		$('#section_SendBTC').hide().next().hide();
		$('#section_TX').hide().next().hide();
		$('#section_Settings').next().show();

		app.clearTransactions();
	}

	this.onGetInfo = function(info) {
		var sNetwork = "Bitcoin";
		if(info.testnet) {
			sNetwork = "Testnet";
		}

		$('#title').text(sNetwork + " on " + app.bitcoin.RPCHost);

		var serverInfo = $('#serverInfo table');

		serverInfo.children().remove();

		for (var key in info) {
			serverInfo.append('
' + key.capitalize() + '	' + info[key] + '
');
		}
		$('#serverInfo tr:odd').addClass('odd');
	}

	this.onSendBTC = function(result, error) {
		if(error != null) {
			app.error(error.message);
			return;
		}
		var obj;
		obj	= setFormValue($('form#sendBTC'), "address", "");
		hideValidation(obj);

		obj = setFormValue($('form#sendBTC'), "amount", "");
		hideValidation(obj);

		app.notify("Bitcoins sent");
		app.refreshAccount();
	}

	this.onValidateAddressField = function(result) {
		var field = $('form#sendBTC input[name="address"]')

		if(result.isvalid && field.val() == result.address)
			showValidation(field, true);
		else
			showValidation(field, false);
	}

	this.onListTransactions = function(transactions) {
		for (var key in transactions)
			if (transactions[key].time == undefined)
				transactions[key].time = 0;

		transactions.sort(sortTransactions);

		app.clearTransactions();

		var txlistContainer = $('#txlist');

		if(txlistContainer.children('tbody').length == 0)
			txlistContainer.append('
');

		var txlist = txlistContainer.children('tbody');

		for (var key in transactions)
			app.addTransaction(txlist, transactions[key]);

		$('#txlist tbody tr:odd').addClass('odd');
	}

	this.clearTransactions = function() {
		$('#txlist tbody').children().remove();
	}

	this.addTransaction = function(txlist, tx) {
		var rowClass = tx.confirmations==0?' class="unconfirmed"':'';
		var confirmations = tx.confirmations<10?tx.confirmations:'✓';

		var timestamp = new Date();
		timestamp.setTime (tx.time * 1000);

		var info = tx.category.capitalize();
		var html = '
' + confirmations + '	' + timestamp.format(this.dateFormat) + '	' + info + '	<td class="' + (tx.amount<0?'debit':'credit') + ' right">' + tx.amount.formatBTC(true) + '
';

		txlist.append(html);
	}

	this.refreshAccount = function() {
		clearTimeout(this.refreshTimeout);

		if(!this.connected) {
			return;
		}

		this.refreshServerInfo();
		this.refreshBalance();
		this.refreshTransactions();
		this.refreshAddress();

		this.refreshTimeout = setTimeout("app.refreshAccount();", 1000);
	}

	this.refreshServerInfo = function() {
		this.bitcoin.getInfo();
	}

	this.refreshTransactions = function() {
		this.bitcoin.listTransactions(this.account);
	}

	this.refreshBalance = function() {
		this.bitcoin.getBalance(this.account);
	}

	this.refreshAddress = function() {
		this.bitcoin.getAddress(this.account);
	}

	this.connect = function(host, port, user, pass) {
		this.onDisconnect();
		this.notify("Connecting");
		this.bitcoin = new Bitcoin(this, host, port, user, pass);
		this.bitcoin.init();
		this.bitcoin.connect();
	}

	this.error = function(msg) {
		$(window).humanMsg(msg);
	}

	this.notify = function(msg) {
		$(window).humanMsg(msg);
	}

	this.sendBTC = function(address, amount) {
		if(!this.connected) {
			return this.error("Not connected!");
		}

		if(address === "") {
			return this.error("Invalid bitcoin address");
		}

		amount = Math.round(amount*100)/100;
		var confString = "Send " + amount.formatBTC() + " to " + address + "?";

		if(confirm(confString)) {
			app.bitcoin.sendBTC(this.account, '"' + address + '"', amount);
		}
	}

	this.addPrototypes = function() {
		String.prototype.capitalize = function() {
			    return this.charAt(0).toUpperCase() + this.slice(1);
		}

		Number.prototype.formatBTC = function(addSign) {
			var nf = new NumberFormat(this);
			nf.setPlaces(2);
			nf.setCurrency(true);
			nf.setCurrencyValue(" BTC");
			nf.setCurrencyPosition(nf.RIGHT_OUTSIDE);

			var s = nf.toFormatted();

			if(addSign && this > 0)
				s = "+" + s;

			return s;
		}
	}

	this.init = function() {
		this.addPrototypes();

		if(!this.connected) {
			$('#accountInfo').hide();

			$.getJSON('settings.json', function(data) {
						if(data) {
							var form = $('form#settingsServer');
							setFormValue(form, "host", data.host);
							setFormValue(form, "port", data.port);
							setFormValue(form, "user", data.user);
							setFormValue(form, "pass", data.pass);
						}
					});
			this.onDisconnect();
		}

		var hostname = window.location.hostname;

		$('#disconnectButton').click( function() {
					app.onDisconnect();
					return false;
				});

		$('form#settingsServer').submit( function() {
					var host = getFormValue(this, "host");
					var port = getFormValue(this, "port");
					var user = getFormValue(this, "user");
					var pass = getFormValue(this, "pass");
					app.connect(host, port, user, pass);
					return false;
				});

		$('form#sendBTC input[name="address"]').change( function() {
					if($(this).val() === "") {
						hideValidation(this);
						return;
					}

					var address = $(this).val();

					app.bitcoin.validateAddress(app.onValidateAddressField, address);
				});

		$('form#sendBTC input[name="amount"]').change( function() {
					if($(this).val() === "") {
						hideValidation(this);
						return;
					}

					var amount = $(this).val();

					if(amount > 0 && amount <= app.balance)
						showValidation(this, true);
					else
						showValidation(this, false);
				});

		$('form#sendBTC').submit( function() {
					var address = getFormValue(this, "address");
					var amount = getFormValue(this, "amount");

					app.sendBTC(address, amount);
					return false;
				});
	}
}
<commit_msg>remove unused var<commit_after>function Bitcoin(app, host, port, user, pass) {
	this.app = app;

	this.RPCHost = host;
	this.RPCPort = port;
	this.RPCUser = user;
	this.RPCPass = pass;
	this.RPCURL;

	this.prepareURL = function() {
		var url = "http://";

		if(this.RPCUser) {
			url += this.RPCUser;
			if(this.RPCPass) {
				url += ":" + this.RPCPass;
			}
			url += "@";
		}

		url += this.RPCHost;

		if(this.RPCPort) {
			url += ":" + this.RPCPort;
		}

		url += "/?callback=?";

		return url;
	}

	this.RPC = function(method, params, callback) {
		var request;

		if(params != null) {
			for(var key in params) {
				params[key] = params[key];
			}
			request = {method: method, params: params};
		} else {
			request = {method: method};
		}

		jQuery.getJSON(this.RPCURL, request, function(data) {
					callback(data.result, data.error);
				});
	}

	this.listTransactions = function(account) {
		this.RPC("listtransactions", ['"' + account + '"', 999999], this.app.onListTransactions);
	}

	this.validateAddress = function(callback, address) {
		this.RPC("validateaddress", ['"' + address + '"'], callback);
	}

	this.sendBTC = function(account, address, amount) {
		this.RPC("sendfrom", ['"' + account + '"', address, amount], this.app.onSendBTC);
	}

	this.getAddress = function(account) {
		this.RPC("getaccountaddress", ['"' + account + '"'], this.app.onGetAddress);
	}

	this.getBalance = function(account) {
		this.RPC("getbalance", ['"' + account + '"'], this.app.onGetBalance);
	}

	this.getInfo = function() {
		this.RPC("getinfo", null, this.app.onGetInfo);
	}

	this.connect = function() {
		this.RPC("getinfo", null, this.app.onConnect);
	}

	this.init = function() {
		this.RPCURL = this.prepareURL();
	}
}

function setFormValue(form, name, value) {
	var obj = $(form).children('input[name="' + name + '"]');
	obj.val(value);

	return obj;
}

function getFormValue(form, name) {
	return $(form).children('input[name="' + name + '"]').val();
}

function sortTransactions(a, b) {
	if(b.time != a.time)
		return (b.time - a.time);

	if(b.category != a.category)
		return (a.category < b.category) ? -1 : 1;

	return (a.amount - b.amount);
}

function hideValidation(obj) {
	var o = $(obj).next('span').removeClass().text('');
}

function showValidation(obj, correct) {
	var o = $(obj).next('span');

	if(correct) {
		o.removeClass().addClass('valRight').html("✔");
	} else {
		o.removeClass().addClass('valWrong').html("✘");
	}
}

/* Because of the way JSONP works this codes assumes a global
 * variable named 'app' pointing to the BitcoinApp() instance!
 *
 *
 */

function BitcoinApp() {
	this.bitcoin = false;
	this.account = "";
	this.balance;
	this.connected = false;
	this.refreshTimeout = false;
	this.dateFormat = "dd/mm/yyyy HH:MM";

	this.onGetBalance = function(balance) {
		$('#balance').text(balance.formatBTC());
		app.balance = balance;
	}

	this.onGetAddress = function(address) {
		var addressField = $('#address');
		if(addressField.text() != address)
			$('#address').text(address);
	}

	this.onConnect = function(info) {
		if(info.version) {
			app.connected = true;

			app.refreshAccount();

			$('#section_Settings').next().slideUp('fast');
			$('#accountInfo').slideDown('fast');
			$('#section_SendBTC').show();
			$('#section_TX').show().next().show();
			$('#serverInfo').show();
		}
	}

	this.onDisconnect = function() {
		app.connected = false;
		app.balance = false;

		$('#title').text("Bitcoin (not connected)");
		$('#accountInfo').slideUp('fast');
		$('#serverInfo').hide();
		$('#serverInfo table').children().remove();
		$('#section_SendBTC').hide().next().hide();
		$('#section_TX').hide().next().hide();
		$('#section_Settings').next().show();

		app.clearTransactions();
	}

	this.onGetInfo = function(info) {
		var sNetwork = "Bitcoin";
		if(info.testnet) {
			sNetwork = "Testnet";
		}

		$('#title').text(sNetwork + " on " + app.bitcoin.RPCHost);

		var serverInfo = $('#serverInfo table');

		serverInfo.children().remove();

		for (var key in info) {
			serverInfo.append('
' + key.capitalize() + '	' + info[key] + '
');
		}
		$('#serverInfo tr:odd').addClass('odd');
	}

	this.onSendBTC = function(result, error) {
		if(error != null) {
			app.error(error.message);
			return;
		}
		var obj;
		obj	= setFormValue($('form#sendBTC'), "address", "");
		hideValidation(obj);

		obj = setFormValue($('form#sendBTC'), "amount", "");
		hideValidation(obj);

		app.notify("Bitcoins sent");
		app.refreshAccount();
	}

	this.onValidateAddressField = function(result) {
		var field = $('form#sendBTC input[name="address"]')

		if(result.isvalid && field.val() == result.address)
			showValidation(field, true);
		else
			showValidation(field, false);
	}

	this.onListTransactions = function(transactions) {
		for (var key in transactions)
			if (transactions[key].time == undefined)
				transactions[key].time = 0;

		transactions.sort(sortTransactions);

		app.clearTransactions();

		var txlistContainer = $('#txlist');

		if(txlistContainer.children('tbody').length == 0)
			txlistContainer.append('
');

		var txlist = txlistContainer.children('tbody');

		for (var key in transactions)
			app.addTransaction(txlist, transactions[key]);

		$('#txlist tbody tr:odd').addClass('odd');
	}

	this.clearTransactions = function() {
		$('#txlist tbody').children().remove();
	}

	this.addTransaction = function(txlist, tx) {
		var rowClass = tx.confirmations==0?' class="unconfirmed"':'';
		var confirmations = tx.confirmations<10?tx.confirmations:'✓';

		var timestamp = new Date();
		timestamp.setTime (tx.time * 1000);

		var info = tx.category.capitalize();
		var html = '
' + confirmations + '	' + timestamp.format(this.dateFormat) + '	' + info + '	<td class="' + (tx.amount<0?'debit':'credit') + ' right">' + tx.amount.formatBTC(true) + '
';

		txlist.append(html);
	}

	this.refreshAccount = function() {
		clearTimeout(this.refreshTimeout);

		if(!this.connected) {
			return;
		}

		this.refreshServerInfo();
		this.refreshBalance();
		this.refreshTransactions();
		this.refreshAddress();

		this.refreshTimeout = setTimeout("app.refreshAccount();", 1000);
	}

	this.refreshServerInfo = function() {
		this.bitcoin.getInfo();
	}

	this.refreshTransactions = function() {
		this.bitcoin.listTransactions(this.account);
	}

	this.refreshBalance = function() {
		this.bitcoin.getBalance(this.account);
	}

	this.refreshAddress = function() {
		this.bitcoin.getAddress(this.account);
	}

	this.connect = function(host, port, user, pass) {
		this.onDisconnect();
		this.notify("Connecting");
		this.bitcoin = new Bitcoin(this, host, port, user, pass);
		this.bitcoin.init();
		this.bitcoin.connect();
	}

	this.error = function(msg) {
		$(window).humanMsg(msg);
	}

	this.notify = function(msg) {
		$(window).humanMsg(msg);
	}

	this.sendBTC = function(address, amount) {
		if(!this.connected) {
			return this.error("Not connected!");
		}

		if(address === "") {
			return this.error("Invalid bitcoin address");
		}

		amount = Math.round(amount*100)/100;
		var confString = "Send " + amount.formatBTC() + " to " + address + "?";

		if(confirm(confString)) {
			app.bitcoin.sendBTC(this.account, '"' + address + '"', amount);
		}
	}

	this.addPrototypes = function() {
		String.prototype.capitalize = function() {
			    return this.charAt(0).toUpperCase() + this.slice(1);
		}

		Number.prototype.formatBTC = function(addSign) {
			var nf = new NumberFormat(this);
			nf.setPlaces(2);
			nf.setCurrency(true);
			nf.setCurrencyValue(" BTC");
			nf.setCurrencyPosition(nf.RIGHT_OUTSIDE);

			var s = nf.toFormatted();

			if(addSign && this > 0)
				s = "+" + s;

			return s;
		}
	}

	this.init = function() {
		this.addPrototypes();

		if(!this.connected) {
			$('#accountInfo').hide();

			$.getJSON('settings.json', function(data) {
						if(data) {
							var form = $('form#settingsServer');
							setFormValue(form, "host", data.host);
							setFormValue(form, "port", data.port);
							setFormValue(form, "user", data.user);
							setFormValue(form, "pass", data.pass);
						}
					});
			this.onDisconnect();
		}

		$('#disconnectButton').click( function() {
					app.onDisconnect();
					return false;
				});

		$('form#settingsServer').submit( function() {
					var host = getFormValue(this, "host");
					var port = getFormValue(this, "port");
					var user = getFormValue(this, "user");
					var pass = getFormValue(this, "pass");
					app.connect(host, port, user, pass);
					return false;
				});

		$('form#sendBTC input[name="address"]').change( function() {
					if($(this).val() === "") {
						hideValidation(this);
						return;
					}

					var address = $(this).val();

					app.bitcoin.validateAddress(app.onValidateAddressField, address);
				});

		$('form#sendBTC input[name="amount"]').change( function() {
					if($(this).val() === "") {
						hideValidation(this);
						return;
					}

					var amount = $(this).val();

					if(amount > 0 && amount <= app.balance)
						showValidation(this, true);
					else
						showValidation(this, false);
				});

		$('form#sendBTC').submit( function() {
					var address = getFormValue(this, "address");
					var amount = getFormValue(this, "amount");

					app.sendBTC(address, amount);
					return false;
				});
	}
}
<|endoftext|>


<commit_before>/**
 * @file Chain agent interfacing with Etherscan.
 * @module chain/etherscan
 */

'use strict'

// Imports
const config = require('config')

/**
 * Private members store.
 * @private
 */
const privs = new WeakMap()

/**
 * Chain agent interfacing with Etherscan.
 */
class ChainAgent {
  /**
   * No parameters.
   */
  constructor () {
    const priv = {}
    privs.set(this, priv)
    priv.apiKey = config.get('Etherscan.apiKey')
  }
}

// Expose
module.exports = ChainAgent
<commit_msg>Label Etherscan ChainAgent static<commit_after>/**
 * @file Chain agent interfacing with Etherscan.
 * @module chain/etherscan
 */

'use strict'

// Imports
const config = require('config')

/**
 * Private members store.
 * @private
 */
const privs = new WeakMap()

/**
 * Chain agent interfacing with Etherscan.
 * @static
 */
class ChainAgent {
  /**
   * No parameters.
   */
  constructor () {
    const priv = {}
    privs.set(this, priv)
    priv.apiKey = config.get('Etherscan.apiKey')
  }
}

// Expose
module.exports = ChainAgent
<|endoftext|>


<commit_before>(function () { /*global _: false, Backbone: false */
    // Generate four random hex digits.
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    // Generate a pseudo-GUID by concatenating random hexadecimal.
    function guid() {
        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    }

    // Naming is a mess!
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
    var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; // No prefix in moz
    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; // No prefix in moz

    /* Horrible Hack to prevent ' Expected an identifier and instead saw 'continue' (a reserved word).'*/
    if (window.indexedDB) {
         indexedDB.prototype._continue =  indexedDB.prototype.continue;
    } else if (window.webkitIDBRequest) {
        webkitIDBRequest.prototype._continue = webkitIDBRequest.prototype.continue;
    } else if(window.mozIndexedDB) {
        mozIndexedDB.prototype._continue = mozIndexedDB.prototype.continue;
    }

    // Driver object
    // That's the interesting part.
    // There is a driver for each schema provided. The schema is a te combination of name (for the database), a version as well as migrations to reach that
    // version of the database.
    function Driver(schema, ready) {
        this.schema         = schema;
        this.ready          = ready;
        this.error          = null;
        this.transactions   = []; // Used to list all transactions and keep track of active ones.
        this.db             = null;
        this.dbRequest      = indexedDB.open(this.schema.id, this.schema.description || "");

        /* DEBUG PURPOSES */
        window.onbeforeunload = function() {
            // db.close();
            chrome.extension.sendRequest({ signature: "debug", params: {message: "DIE"}}, function (response) {});
            // this.transactions.forEach(function(trans) {
            //     chrome.extension.sendRequest({ signature: "debug", params: {message: "abort"}}, function (response) {});
            //     trans.abort();
            // });
            // this.connection.close();

        }
        /* DEBUG PURPOSES */

        this.dbRequest.onsuccess = function (e) {
            this.db = e.target.result; // Attach the connection ot the queue.

            if (this.db.version === _.last(this.schema.migrations).version) {
                // No migration to perform!
                this.ready();
            } else if (this.db.version < _.last(this.schema.migrations).version) {
                // We need to migrate up to the current migration defined in the database
                this.migrate(this.schema.migrations, this.db.version, {
                    success: function () {
                        this.ready();
                    }.bind(this),
                    error: function () {
                        this.error = "Database not up to date. " + this.db.version + " expected was " + _.last(this.schema.migrations).version;
                    }.bind(this)
                });
            } else {
                // Looks like the IndexedDB is at a higher version than the current driver schema.
                this.error = "Database version is greater than current code " + this.db.version + " expected was " + _.last(this.schema.migrations).version;
            }
        }.bind(this);

        this.dbRequest.onerror = function (e) {
            // Failed to open the database
            this.error = "Couldn't not connect to the database"
        }.bind(this);

        this.dbRequest.onabort = function (e) {
            // Failed to open the database
            this.error = "Connection to the database aborted"
        }.bind(this);
    }

    function debug_log(str) {
        if (typeof window.console !== "undefined" && typeof window.console.log !== "undefined") {
            window.console.log(str);
        }
    }

    // Driver Prototype
    Driver.prototype = {

        // Tracks transactions. Mostly for debugging purposes. TO-IMPROVE
        _track_transaction: function(transaction) {
            this.transactions.push(transaction);
            function removeIt() {
                var idx = this.transactions.indexOf(transaction);
                if (idx !== -1) {this.transactions.splice(idx); }
            };
            transaction.oncomplete = removeIt.bind(this);
            transaction.onabort = removeIt.bind(this);
            transaction.onerror = removeIt.bind(this);
        },

        // Performs all the migrations to reach the right version of the database.
        migrate: function (migrations, version, options) {
            debug_log("Starting migrations from " + version);
            this._migrate_next(migrations, version, options);
        },

        // Performs the next migrations. This method is private and should probably not be called.
        _migrate_next: function (migrations, version, options) {
            var that = this;
            var migration = migrations.shift();
            if (migration) {
                if (!version || version < migration.version) {
                    // We need to apply this migration-
                    if (typeof migration.before == "undefined") {
                        migration.before = function (next) {
                            next();
                        };
                    }
                    if (typeof migration.after == "undefined") {
                        migration.after = function (next) {
                            next();
                        };
                    }
                    // First, let's run the before script
                    migration.before(function () {
                        var versionRequest = this.db.setVersion(migration.version);
                        versionRequest.onsuccess = function (e) {
                            var transaction = versionRequest.result;
                            this._track_transaction(transaction);

                            migration.migrate(this.db, versionRequest, function () {
                                // Migration successfully appliedn let's go to the next one!
                                migration.after(function () {
                                    debug_log("Migrated to " + migration.version);
                                    that._migrate_next(migrations, version, options);
                                });
                            });
                        }.bind(this);
                    }.bind(this));
                } else {
                    // No need to apply this migration
                    debug_log("Skipping migration " + migration.version);
                    this._migrate_next(migrations, version, options);
                }
            } else {
                debug_log("Done migrating");
                // No more migration
                options.success();
            }
        },

        // This is the main method, called by the ExecutionQueue when the driver is ready (database open and migration performed)
        execute: function (storeName, method, object, options) {
            switch (method) {
            case "create":
                this.write(storeName, object, options);
                break;
            case "read":
                if (object instanceof Backbone.Collection) {
                    this.query(storeName, object, options); // It's a collection
                } else {
                    this.read(storeName, object, options); // It's a model
                }
                break;
            case "update":
                this.write(storeName, object, options); // We may want to check that this is not a collection. TOFIX
                break;
            case "delete":
                this.delete(storeName, object, options); // We may want to check that this is not a collection. TOFIX
                break;
            default:
                // Hum what?
            }
        },

        // Writes the json to the storeName in db.
        // options are just success and error callbacks.
        write: function (storeName, object, options) {
            var writeTransaction = this.db.transaction([storeName], IDBTransaction.READ_WRITE);
            this._track_transaction(writeTransaction);
            var store = writeTransaction.objectStore(storeName);
            var json = object.toJSON();

            if (!json.id) json.id = guid();

            var writeRequest = store.put(json, json.id);

            writeRequest.onerror = function (e) {
                options.error(e);
            };
            writeRequest.onsuccess = function (e) {
                options.success(json);
            };
        },

        // Reads from storeName in db with json.id if it's there of with any json.xxxx as long as xxx is an index in storeName
        read: function (storeName, object, options) {
            var readTransaction = this.db.transaction([storeName], IDBTransaction.READ_ONLY);
            this._track_transaction(readTransaction);

            var store = readTransaction.objectStore(storeName);
            var json = object.toJSON();

            var getRequest = null;
            if (json.id) {
                getRequest = store.get(json.id);
            } else {
                // We need to find which index we have
                _.each(store.indexNames, function (key, index) {
                    index = store.index(key);
                    if (json[index.keyPath] && !getRequest) {
                        getRequest = index.get(json[index.keyPath]);
                    }
                });
            }
            if (getRequest) {
                getRequest.onsuccess = function (event) {
                    if (event.target.result) {
                        options.success(event.target.result);
                    } else {
                        options.error("Not Found");
                    }
                };
                getRequest.onerror = function () {
                    options.error("Not Found"); // We couldn't find the record.
                }
            } else {
                options.error("Not Found"); // We couldn't even look for it, as we don't have enough data.
            }
        },

        // Deletes the json.id key and value in storeName from db.
        delete: function (storeName, object, options) {
            var deleteTransaction = this.db.transaction([storeName], IDBTransaction.READ_WRITE);
            this._track_transaction(deleteTransaction);

            var store = deleteTransaction.objectStore(storeName);
            var json = object.toJSON();

            var deleteRequest = store.delete(json.id);
            deleteRequest.onsuccess = function (event) {
                options.success(null);
            };
            deleteRequest.onerror = function (event) {
                options.error("Not Deleted");
            };
        },

        // Performs a query on storeName in db.
        // options may include :
        // - conditions : value of an index, or range for an index
        // - range : range for the primary key
        // - limit : max number of elements to be yielded
        // - offset : skipped items.
        query: function (storeName, collection, options) {
            var elements = [];
            var skipped = 0, processed = 0;
            var queryTransaction = this.db.transaction([storeName], IDBTransaction.READ_ONLY);
            this._track_transaction(queryTransaction);

            var readCursor = null;
            var store = queryTransaction.objectStore(storeName);
            var index = null,
                lower = null,
                upper = null,
                bounds = null;

            if (options.conditions) {
                // We have a condition, we need to use it for the cursor
                _.each(store.indexNames, function (key) {
                    if (!readCursor) {
                        index = store.index(key);
                        if (options.conditions[index.keyPath] instanceof Array) {
                            lower = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][1] : options.conditions[index.keyPath][0];
                            upper = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][0] : options.conditions[index.keyPath][1];
                            bounds = IDBKeyRange.bound(lower, upper, true, true);

                            if (options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1]) {
                                // Looks like we want the DESC order
                                readCursor = index.openCursor(bounds, 2);
                            } else {
                                // We want ASC order
                                readCursor = index.openCursor(bounds, 0);
                            }
                        } else if (options.conditions[index.keyPath]) {
                            bounds = IDBKeyRange.only(options.conditions[index.keyPath]);
                            readCursor = index.openCursor(bounds);
                        }
                    }
                });
            } else {
                // No conditions, use the index
                if (options.range) {
                    lower = options.range[0] > options.range[1] ? options.range[1] : options.range[0];
                    upper = options.range[0] > options.range[1] ? options.range[0] : options.range[1];
                    bounds = IDBKeyRange.bound(lower, upper);
                    if (options.range[0] > options.range[1]) {
                        readCursor = store.openCursor(bounds, 2);
                    } else {
                        readCursor = store.openCursor(bounds, 0);
                    }
                } else {
                    readCursor = store.openCursor();
                }
            }

            if (typeof (readCursor) == "undefined" || !readCursor) {
                options.error("No Cursor");
            } else {
                // Setup a handler for the cursor’s `success` event:
                readCursor.onsuccess = function (e) {
                    var cursor = e.target.result;
                    if (!cursor) {
                        if (options.addIndividually || options.clear) {
                            // nothing!
                            // We need to indicate that we're done. But, how?
                            collection.trigger("reset");
                        } else {
                            options.success(elements); // We're done. No more elements.
                        }
                    }
                    else {
                        // Cursor is not over yet.
                        if (options.limit && processed >= options.limit) {
                            // Yet, we have processed enough elements. So, let's just skip.
                            if (bounds && options.conditions[index.keyPath]) {
                                cursor.continue(options.conditions[index.keyPath][1] + 1); /* We need to 'terminate' the cursor cleany, by moving to the end */
                            } else {
                                cursor.continue(); /* We need to 'terminate' the cursor cleany, by moving to the end */
                            }
                        }
                        else if (options.offset && options.offset > skipped) {
                            skipped++;
                            cursor.continue(options.offset - skipped); /* We need to Moving the cursor forward */
                        } else {
                            // This time, it looks like it's good!
                            processed++;
                            cursor.continue();
                            if (options.addIndividually) {
                                collection.add(cursor.value);
                            } else if (options.clear) {
                                var deleteRequest = store.delete(cursor.value.id);
                                deleteRequest.onsuccess = function (event) {
                                    elements.push(cursor.value);
                                };
                                deleteRequest.onerror = function (event) {
                                    elements.push(cursor.value);
                                };

                            } else {
                                elements.push(cursor.value);
                            }
                        }
                    }
                };
            }
        }
    };

    // ExecutionQueue object
    // The execution queue is an abstraction to buffer up requests to the database.
    // It holds a "driver". When the driver is ready, it just fires up the queue and executes in sync.
    function ExecutionQueue(schema) {
        this.driver     = new Driver(schema, this.ready.bind(this));
        this.started    = false;
        this.stack      = [];
    }

    // ExecutionQueue Prototype
    ExecutionQueue.prototype = {
        // Called when the driver is ready
        // It just loops over the elements in the queue and executes them.
        ready: function () {
            this.started = true;
            _.each(this.stack, function (message) {
                this.execute(message);
            }.bind(this));
        },

        // Executes a given command on the driver. If not started, just stacks up one more element.
        execute: function (message) {
            if (this.started) {
                this.driver.execute(message[1].storeName, message[0], message[1], message[2]); // Upon messages, we execute the query
            } else {
                this.stack.push(message);
            }
        }
    };

    // Method used by Backbone for sync of data with data store. It was initially designed to work with "server side" APIs, This wrapper makes
    // it work with the local indexedDB stuff. It uses the schema attribute provided by the object.
    // The wrapper keeps an active Executuon Queue for each "schema", and executes querues agains it, based on the object type (collection or
    // single model), but also the method... etc.
    // Keeps track of the connections
    var Databases = {};

    Backbone.sync = function (method, object, options) {
        var schema = object.database;
        if (!Databases[schema.id]) {
            Databases[schema.id] = new ExecutionQueue(schema);
        }
        Databases[schema.id].execute([method, object, options]);
    };
})();<commit_msg>removed chrome specific debugging lines<commit_after>(function () { /*global _: false, Backbone: false */
    // Generate four random hex digits.
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    // Generate a pseudo-GUID by concatenating random hexadecimal.
    function guid() {
        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    }

    // Naming is a mess!
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
    var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; // No prefix in moz
    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; // No prefix in moz

    /* Horrible Hack to prevent ' Expected an identifier and instead saw 'continue' (a reserved word).'*/
    if (window.indexedDB) {
         indexedDB.prototype._continue =  indexedDB.prototype.continue;
    } else if (window.webkitIDBRequest) {
        webkitIDBRequest.prototype._continue = webkitIDBRequest.prototype.continue;
    } else if(window.mozIndexedDB) {
        mozIndexedDB.prototype._continue = mozIndexedDB.prototype.continue;
    }

    // Driver object
    // That's the interesting part.
    // There is a driver for each schema provided. The schema is a te combination of name (for the database), a version as well as migrations to reach that
    // version of the database.
    function Driver(schema, ready) {
        this.schema         = schema;
        this.ready          = ready;
        this.error          = null;
        this.transactions   = []; // Used to list all transactions and keep track of active ones.
        this.db             = null;
        this.dbRequest      = indexedDB.open(this.schema.id, this.schema.description || "");

        this.dbRequest.onsuccess = function (e) {
            this.db = e.target.result; // Attach the connection ot the queue.

            if (this.db.version === _.last(this.schema.migrations).version) {
                // No migration to perform!
                this.ready();
            } else if (this.db.version < _.last(this.schema.migrations).version) {
                // We need to migrate up to the current migration defined in the database
                this.migrate(this.schema.migrations, this.db.version, {
                    success: function () {
                        this.ready();
                    }.bind(this),
                    error: function () {
                        this.error = "Database not up to date. " + this.db.version + " expected was " + _.last(this.schema.migrations).version;
                    }.bind(this)
                });
            } else {
                // Looks like the IndexedDB is at a higher version than the current driver schema.
                this.error = "Database version is greater than current code " + this.db.version + " expected was " + _.last(this.schema.migrations).version;
            }
        }.bind(this);

        this.dbRequest.onerror = function (e) {
            // Failed to open the database
            this.error = "Couldn't not connect to the database"
        }.bind(this);

        this.dbRequest.onabort = function (e) {
            // Failed to open the database
            this.error = "Connection to the database aborted"
        }.bind(this);
    }

    function debug_log(str) {
        if (typeof window.console !== "undefined" && typeof window.console.log !== "undefined") {
            window.console.log(str);
        }
    }

    // Driver Prototype
    Driver.prototype = {

        // Tracks transactions. Mostly for debugging purposes. TO-IMPROVE
        _track_transaction: function(transaction) {
            this.transactions.push(transaction);
            function removeIt() {
                var idx = this.transactions.indexOf(transaction);
                if (idx !== -1) {this.transactions.splice(idx); }
            };
            transaction.oncomplete = removeIt.bind(this);
            transaction.onabort = removeIt.bind(this);
            transaction.onerror = removeIt.bind(this);
        },

        // Performs all the migrations to reach the right version of the database.
        migrate: function (migrations, version, options) {
            debug_log("Starting migrations from " + version);
            this._migrate_next(migrations, version, options);
        },

        // Performs the next migrations. This method is private and should probably not be called.
        _migrate_next: function (migrations, version, options) {
            var that = this;
            var migration = migrations.shift();
            if (migration) {
                if (!version || version < migration.version) {
                    // We need to apply this migration-
                    if (typeof migration.before == "undefined") {
                        migration.before = function (next) {
                            next();
                        };
                    }
                    if (typeof migration.after == "undefined") {
                        migration.after = function (next) {
                            next();
                        };
                    }
                    // First, let's run the before script
                    migration.before(function () {
                        var versionRequest = this.db.setVersion(migration.version);
                        versionRequest.onsuccess = function (e) {
                            var transaction = versionRequest.result;
                            this._track_transaction(transaction);

                            migration.migrate(this.db, versionRequest, function () {
                                // Migration successfully appliedn let's go to the next one!
                                migration.after(function () {
                                    debug_log("Migrated to " + migration.version);
                                    that._migrate_next(migrations, version, options);
                                });
                            });
                        }.bind(this);
                    }.bind(this));
                } else {
                    // No need to apply this migration
                    debug_log("Skipping migration " + migration.version);
                    this._migrate_next(migrations, version, options);
                }
            } else {
                debug_log("Done migrating");
                // No more migration
                options.success();
            }
        },

        // This is the main method, called by the ExecutionQueue when the driver is ready (database open and migration performed)
        execute: function (storeName, method, object, options) {
            switch (method) {
            case "create":
                this.write(storeName, object, options);
                break;
            case "read":
                if (object instanceof Backbone.Collection) {
                    this.query(storeName, object, options); // It's a collection
                } else {
                    this.read(storeName, object, options); // It's a model
                }
                break;
            case "update":
                this.write(storeName, object, options); // We may want to check that this is not a collection. TOFIX
                break;
            case "delete":
                this.delete(storeName, object, options); // We may want to check that this is not a collection. TOFIX
                break;
            default:
                // Hum what?
            }
        },

        // Writes the json to the storeName in db.
        // options are just success and error callbacks.
        write: function (storeName, object, options) {
            var writeTransaction = this.db.transaction([storeName], IDBTransaction.READ_WRITE);
            this._track_transaction(writeTransaction);
            var store = writeTransaction.objectStore(storeName);
            var json = object.toJSON();

            if (!json.id) json.id = guid();

            var writeRequest = store.put(json, json.id);

            writeRequest.onerror = function (e) {
                options.error(e);
            };
            writeRequest.onsuccess = function (e) {
                options.success(json);
            };
        },

        // Reads from storeName in db with json.id if it's there of with any json.xxxx as long as xxx is an index in storeName
        read: function (storeName, object, options) {
            var readTransaction = this.db.transaction([storeName], IDBTransaction.READ_ONLY);
            this._track_transaction(readTransaction);

            var store = readTransaction.objectStore(storeName);
            var json = object.toJSON();

            var getRequest = null;
            if (json.id) {
                getRequest = store.get(json.id);
            } else {
                // We need to find which index we have
                _.each(store.indexNames, function (key, index) {
                    index = store.index(key);
                    if (json[index.keyPath] && !getRequest) {
                        getRequest = index.get(json[index.keyPath]);
                    }
                });
            }
            if (getRequest) {
                getRequest.onsuccess = function (event) {
                    if (event.target.result) {
                        options.success(event.target.result);
                    } else {
                        options.error("Not Found");
                    }
                };
                getRequest.onerror = function () {
                    options.error("Not Found"); // We couldn't find the record.
                }
            } else {
                options.error("Not Found"); // We couldn't even look for it, as we don't have enough data.
            }
        },

        // Deletes the json.id key and value in storeName from db.
        delete: function (storeName, object, options) {
            var deleteTransaction = this.db.transaction([storeName], IDBTransaction.READ_WRITE);
            this._track_transaction(deleteTransaction);

            var store = deleteTransaction.objectStore(storeName);
            var json = object.toJSON();

            var deleteRequest = store.delete(json.id);
            deleteRequest.onsuccess = function (event) {
                options.success(null);
            };
            deleteRequest.onerror = function (event) {
                options.error("Not Deleted");
            };
        },

        // Performs a query on storeName in db.
        // options may include :
        // - conditions : value of an index, or range for an index
        // - range : range for the primary key
        // - limit : max number of elements to be yielded
        // - offset : skipped items.
        query: function (storeName, collection, options) {
            var elements = [];
            var skipped = 0, processed = 0;
            var queryTransaction = this.db.transaction([storeName], IDBTransaction.READ_ONLY);
            this._track_transaction(queryTransaction);

            var readCursor = null;
            var store = queryTransaction.objectStore(storeName);
            var index = null,
                lower = null,
                upper = null,
                bounds = null;

            if (options.conditions) {
                // We have a condition, we need to use it for the cursor
                _.each(store.indexNames, function (key) {
                    if (!readCursor) {
                        index = store.index(key);
                        if (options.conditions[index.keyPath] instanceof Array) {
                            lower = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][1] : options.conditions[index.keyPath][0];
                            upper = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][0] : options.conditions[index.keyPath][1];
                            bounds = IDBKeyRange.bound(lower, upper, true, true);

                            if (options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1]) {
                                // Looks like we want the DESC order
                                readCursor = index.openCursor(bounds, 2);
                            } else {
                                // We want ASC order
                                readCursor = index.openCursor(bounds, 0);
                            }
                        } else if (options.conditions[index.keyPath]) {
                            bounds = IDBKeyRange.only(options.conditions[index.keyPath]);
                            readCursor = index.openCursor(bounds);
                        }
                    }
                });
            } else {
                // No conditions, use the index
                if (options.range) {
                    lower = options.range[0] > options.range[1] ? options.range[1] : options.range[0];
                    upper = options.range[0] > options.range[1] ? options.range[0] : options.range[1];
                    bounds = IDBKeyRange.bound(lower, upper);
                    if (options.range[0] > options.range[1]) {
                        readCursor = store.openCursor(bounds, 2);
                    } else {
                        readCursor = store.openCursor(bounds, 0);
                    }
                } else {
                    readCursor = store.openCursor();
                }
            }

            if (typeof (readCursor) == "undefined" || !readCursor) {
                options.error("No Cursor");
            } else {
                // Setup a handler for the cursor’s `success` event:
                readCursor.onsuccess = function (e) {
                    var cursor = e.target.result;
                    if (!cursor) {
                        if (options.addIndividually || options.clear) {
                            // nothing!
                            // We need to indicate that we're done. But, how?
                            collection.trigger("reset");
                        } else {
                            options.success(elements); // We're done. No more elements.
                        }
                    }
                    else {
                        // Cursor is not over yet.
                        if (options.limit && processed >= options.limit) {
                            // Yet, we have processed enough elements. So, let's just skip.
                            if (bounds && options.conditions[index.keyPath]) {
                                cursor.continue(options.conditions[index.keyPath][1] + 1); /* We need to 'terminate' the cursor cleany, by moving to the end */
                            } else {
                                cursor.continue(); /* We need to 'terminate' the cursor cleany, by moving to the end */
                            }
                        }
                        else if (options.offset && options.offset > skipped) {
                            skipped++;
                            cursor.continue(options.offset - skipped); /* We need to Moving the cursor forward */
                        } else {
                            // This time, it looks like it's good!
                            processed++;
                            cursor.continue();
                            if (options.addIndividually) {
                                collection.add(cursor.value);
                            } else if (options.clear) {
                                var deleteRequest = store.delete(cursor.value.id);
                                deleteRequest.onsuccess = function (event) {
                                    elements.push(cursor.value);
                                };
                                deleteRequest.onerror = function (event) {
                                    elements.push(cursor.value);
                                };

                            } else {
                                elements.push(cursor.value);
                            }
                        }
                    }
                };
            }
        }
    };

    // ExecutionQueue object
    // The execution queue is an abstraction to buffer up requests to the database.
    // It holds a "driver". When the driver is ready, it just fires up the queue and executes in sync.
    function ExecutionQueue(schema) {
        this.driver     = new Driver(schema, this.ready.bind(this));
        this.started    = false;
        this.stack      = [];
    }

    // ExecutionQueue Prototype
    ExecutionQueue.prototype = {
        // Called when the driver is ready
        // It just loops over the elements in the queue and executes them.
        ready: function () {
            this.started = true;
            _.each(this.stack, function (message) {
                this.execute(message);
            }.bind(this));
        },

        // Executes a given command on the driver. If not started, just stacks up one more element.
        execute: function (message) {
            if (this.started) {
                this.driver.execute(message[1].storeName, message[0], message[1], message[2]); // Upon messages, we execute the query
            } else {
                this.stack.push(message);
            }
        }
    };

    // Method used by Backbone for sync of data with data store. It was initially designed to work with "server side" APIs, This wrapper makes
    // it work with the local indexedDB stuff. It uses the schema attribute provided by the object.
    // The wrapper keeps an active Executuon Queue for each "schema", and executes querues agains it, based on the object type (collection or
    // single model), but also the method... etc.
    // Keeps track of the connections
    var Databases = {};

    Backbone.sync = function (method, object, options) {
        var schema = object.database;
        if (!Databases[schema.id]) {
            Databases[schema.id] = new ExecutionQueue(schema);
        }
        Databases[schema.id].execute([method, object, options]);
    };
})();<|endoftext|>


<commit_before>const inquirer = require('inquirer');
const request = require('request');
const async = require('async');
const cheerio = require('cheerio');
const keysMethods = require('./keys.js');
const promise = require('bluebird');

const apiEndpointGetNumberOfRecords = 'http://localhost:8000/raw-postings?date=';

//============== utilities =================
//==========================================

parseTechnologies = (str, tech) => {
  Object.keys(technologyPatterns).forEach((technology) => {
    hasTechnologies[technology] = technologyPatterns[technology].test(str);
  });
  return hasTechnologies;
};

//================ TEMP ====================
//==========================================

const tempGeo = {
  // countries > states > hubs > sources > queries
  united_states: {
    arizona: {
      phoenix: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-phoenix,az'
        }
      }
    },
    california: {
      san_jose: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-jose,ca'
        }
      },
      san_francisco: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-francisco,ca'
        }
      },
      los_angeles: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-los-angeles,ca'
        }
      },
      san_diego: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-diego,ca'
        }
      }
    },
    colorado: {
      boulder: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-boulder,co'
        }
      },
      fort_collins: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-fort-collins,co'
        }
      }
    },
    georgia: {
      atlanta: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-atlanta,ga'
        }
      }
    },
    illinois: {
      chicago: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-chicago,il'
        }
      }
    },
    kansas: {
      kansas_city: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-kansas-city,ks'
        }
      }
    },
    massachusetts: {
      boston: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-boston,ma'
        }
      }
    },
    new_mexico: {
      albuquerque: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-albuquerque,nm'
        }
      }
    },
    new_york: {
      new_york: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-new-york,ny'
        }
      }
    },
    north_carolina: {
      raleigh: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-raleigh,nc'
        }
      }
    },
    oregon: {
      portland: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-portland,or'
        }
      }
    },
    pennsylvania: {
      philadelphia: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-philadelphia,pa'
        }
      },
      pittsburg: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-pittsburgh,pa'
        }
      }
    },
    tennessee: {
      memphis: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-memphis,tn'
        }
      },
      nashville: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-nashville,tn'
        }
      }
    },
    texas: {
      austin: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-austin,tx'
        }
      },
      houston: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-houston,tx'
        }
      },
      dallas_fort_worth: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-dallas-fort-worth,tx'
        }
      }
    },
    utah: {
      provo: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-provo,ut'
        }
      },
      salt_lake_city: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-salt-lake-city,ut'
        }
      }
    },
    washington: {
      seattle: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-seattle,wa'
        }
      }
    },
    washington_dc: {
      washington_dc: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-washington,dc'
        }
      }
    }
  }
};

const tempGetHubs = () => {
  var hubs = {}, geo = tempGeo;

  for (var country in geo) {
    for (var state in geo[country]) {
      for (var hub in geo[country][state]) {
        hubs[hub] = {};
      }
    }
  }

  return hubs;
};

//================ result ==================
//==========================================

//========= js frameworks crunch ===========
//==========================================
const cruncherJSFrameworks = () => {

  // init: define a records object to hold the data to be stored in prod
  var records = tempGetHubs();
  // var records = keysMethods.getHubs(); // TODO

  // init: store a reference to the view currently being operated on
  const view = 'javascriptFrameworks';

  // init: attach this view to each hub in records
  for (var hub in records) records[hub][view] = [];

  console.log('records: ', records);

  // init: store a list of all the tech tracked for this view
  const tech = keysMethods.getTech(view);

  // create array to store fetched date id's
  const dateIds = keysMethods.getDateIds(); // TODO: dynamic
  console.log('date ids: ', dateIds);
  // get all data id's and populate

  // for each date in the dateIds array:
  dateIds.forEach((dateId) => {

    console.log('this date id: ', dateId);

    // request length (number of records) for the current date slice and store it
    var numberOfRecords = 0;

    const recordsCountUrl = apiEndpointGetNumberOfRecords + dateId + '&index=-1';

    request.get(recordsCountUrl, (err, res, body) => {
      if (err) {
        console.log('[X] error fetching number of records for a date id', err);
      } else {
        numberOfRecords = body;
        console.log('number of records', numberOfRecords);
        if (numberOfRecords > 0) iterateThroughRecords(numberOfRecords);
      }
    });

    // console.log('date id: ', dateId, 'records: ', numberOfRecords);

    const iterateThroughRecords = (num) => {

      // for each date slice, build a temp storage bin per hub to keep a tech count
      var bins = {};

      // bin storage constructor
      const BinInit = () => {
        var bin = {};
        for (var item in tech) {
          bin[item] = 0;
        }
        return bin;
      };

      // initialize data points in each bin
      for (var hub in records) bins[hub] = BinInit();

      console.log(bins);

      // request all records for the current date slice
      // for (var i = 0; i < numberOfRecords; i++) {
      //   request
      //     // TODO: need the real url to continue
      //     .get('http://localhost:8000/raw-postings?' + dateId)
      //     .on('error', (err) => {
      //       console.log('[X] error fetching record', err);
      //     })
      //     .on('response', (response) => {
      //       console.log('[ ] record fetched successfully');
      //       parseTechnologies(JSON.stringify(response, tech));
      //       // parse the response for keywords, increment count if found
      //     });
      // }
    };

  });

  async.series([], (err) => {
    if (err) console.log('failed');
  });

  // phoenix: {
  //   javascriptFrameworks: [
  //     { date: 123456789,
  //       data: {
  //         angular: 7,
  //         backbone: 5,
  //         react: 6,
  //         ember: 3,
  //         knockout: 2,
  //         aurelia: 1,
  //         meteor: 0,
  //         polymer: 1,
  //         vue: 0,
  //         mercury: 1
  //       }
  //     },
  //     { date: 987654321,
  //       data: {...}
  //     }
  //   ],
  //   serverTechnologies: [...],
  //   databases: [...]
  // },
  // colorado: {...}

};

cruncherJSFrameworks();
<commit_msg>cruncher: small progress, pulling new endpoint<commit_after>const inquirer = require('inquirer');
const request = require('request');
const async = require('async');
const cheerio = require('cheerio');
const keysMethods = require('./keys.js');
const promise = require('bluebird');

const apiEndpointGetDateIds = 'http://localhost:8000/raw-postings/dates';
const apiEndpointGetNumberOfRecords = 'http://localhost:8000/raw-postings?date=';

//============== utilities =================
//==========================================

parseTechnologies = (str, tech) => {
  Object.keys(technologyPatterns).forEach((technology) => {
    hasTechnologies[technology] = technologyPatterns[technology].test(str);
  });
  return hasTechnologies;
};

//================ TEMP ====================
//==========================================

const tempGeo = {
  // countries > states > hubs > sources > queries
  united_states: {
    arizona: {
      phoenix: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-phoenix,az'
        }
      }
    },
    california: {
      san_jose: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-jose,ca'
        }
      },
      san_francisco: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-francisco,ca'
        }
      },
      los_angeles: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-los-angeles,ca'
        }
      },
      san_diego: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-san-diego,ca'
        }
      }
    },
    colorado: {
      boulder: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-boulder,co'
        }
      },
      fort_collins: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-fort-collins,co'
        }
      }
    },
    georgia: {
      atlanta: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-atlanta,ga'
        }
      }
    },
    illinois: {
      chicago: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-chicago,il'
        }
      }
    },
    kansas: {
      kansas_city: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-kansas-city,ks'
        }
      }
    },
    massachusetts: {
      boston: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-boston,ma'
        }
      }
    },
    new_mexico: {
      albuquerque: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-albuquerque,nm'
        }
      }
    },
    new_york: {
      new_york: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-new-york,ny'
        }
      }
    },
    north_carolina: {
      raleigh: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-raleigh,nc'
        }
      }
    },
    oregon: {
      portland: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-portland,or'
        }
      }
    },
    pennsylvania: {
      philadelphia: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-philadelphia,pa'
        }
      },
      pittsburg: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-pittsburgh,pa'
        }
      }
    },
    tennessee: {
      memphis: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-memphis,tn'
        }
      },
      nashville: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-nashville,tn'
        }
      }
    },
    texas: {
      austin: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-austin,tx'
        }
      },
      houston: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-houston,tx'
        }
      },
      dallas_fort_worth: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-dallas-fort-worth,tx'
        }
      }
    },
    utah: {
      provo: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-provo,ut'
        }
      },
      salt_lake_city: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-salt-lake-city,ut'
        }
      }
    },
    washington: {
      seattle: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-seattle,wa'
        }
      }
    },
    washington_dc: {
      washington_dc: {
        career_builder: {
          web_developer: 'http://www.careerbuilder.com/jobs-web-developer-in-washington,dc'
        }
      }
    }
  }
};

const tempGetHubs = () => {
  var hubs = {}, geo = tempGeo;

  for (var country in geo) {
    for (var state in geo[country]) {
      for (var hub in geo[country][state]) {
        hubs[hub] = {};
      }
    }
  }

  return hubs;
};

//================ result ==================
//==========================================

//========= js frameworks crunch ===========
//==========================================
const cruncherJSFrameworks = () => {

  // init: define a records object to hold the data to be stored in prod
  var records = tempGetHubs();
  // var records = keysMethods.getHubs(); // TODO

  // init: store a reference to the view currently being operated on
  const view = 'javascriptFrameworks';

  // init: attach this view to each hub in records
  for (var hub in records) records[hub][view] = [];

  console.log('records: ', records);

  // init: store a list of all the tech tracked for this view
  const tech = keysMethods.getTech(view);

  request.get(apiEndpointGetDateIds, (err, res, body) => {
    if (err) {
      console.log('[X] error fetching date id\'s');
    } else {

      // store fetched date id's
      const dateIds = body;
      console.log('date ids: ', dateIds);

      // map all date id's to fetch functions to store in async
      // TODO break this out into a component function and not in the main body
      const dates = dateIds.map((date) => {
        return (done) => {

          console.log('this date id: ', date);

          const recordsCountUrl = apiEndpointGetNumberOfRecords + date + '&index=-1';

          request.get(recordsCountUrl, (err, res, body) => {
            if (err) {
              console.log('[X] error fetching number of records for a date id', err);
            } else {

              // request length (number of records) for the current date slice and store it
              const numberOfRecords = body;
              console.log('number of records', numberOfRecords);

              if (numberOfRecords > 0) {

                // for each date slice, build a temp storage bin per hub to keep a tech count
                var bins = {};

                // bin storage constructor
                const BinInit = () => {
                  var bin = {};
                  for (var item in tech) {
                    bin[item] = 0;
                  }
                  return bin;
                };

                // initialize data points in each bin
                for (var hub in records) bins[hub] = BinInit();

                console.log(bins);

                // request all records for the current date slice
                // TODO break this out into a component function and not in the main body
                // for (var i = 0; i < numberOfRecords; i++) {
                //   request
                //     // TODO: need the real url to continue
                //     .get('http://localhost:8000/raw-postings?' + dateId)
                //     .on('error', (err) => {
                //       console.log('[X] error fetching record', err);
                //     })
                //     .on('response', (response) => {
                //       console.log('[ ] record fetched successfully');
                //       parseTechnologies(JSON.stringify(response, tech));
                //       // parse the response for keywords, increment count if found
                //     });
                // }

              }
            }
          });
        };
      });

      async.series(dates, (err) => {
        if (err) console.log('[X] failed JS framework crunch');
      });

    }
  });

  // phoenix: {
  //   javascriptFrameworks: [
  //     { date: 123456789,
  //       data: {
  //         angular: 7,
  //         backbone: 5,
  //         react: 6,
  //         ember: 3,
  //         knockout: 2,
  //         aurelia: 1,
  //         meteor: 0,
  //         polymer: 1,
  //         vue: 0,
  //         mercury: 1
  //       }
  //     },
  //     { date: 987654321,
  //       data: {...}
  //     }
  //   ],
  //   serverTechnologies: [...],
  //   databases: [...]
  // },
  // colorado: {...}

};

cruncherJSFrameworks();
<|endoftext|>


<commit_before>// load the auth variables
var config = require('../config');

// get user info
var https = require('https');
var url = require('url');

var OAuth2Strategy = require('passport-oauth').OAuth2Strategy.Strategy;

module.exports = function(passport) {

    var oauth2 = new OAuth2Strategy(config.services.cern,
        function(accessToken, refreshToken, profile, done){
            var URI = url.parse(config.services.cern.infoURL);
            var user_req = https.request({
                host: URI.hostname,
                path: URI.pathname,
                port: URI.port,
                method: 'GET',
                headers: {
                    Authorization: "Bearer "+accessToken
                }
            }, function(res){
                var body = '';
                res.on('data', function(chunk){ body+= chunk; });
                res.on('end', function(){
                    var profile = JSON.parse(body);
                    console.log('Profile: %j', profile);
                    return done(null, profile);
                });
            }).on('error', function(e){
                console.log("Got an error: ", e);
                return done(e);
            });
        }
    );

    passport.use(oauth2);

    passport.serializeUser(function(profile, done){
        done(null, profile);
    });

    passport.deserializeUser(function(profile, done){
        done(null, profile);
    });

};
<commit_msg>need to add end?<commit_after>// load the auth variables
var config = require('../config');

// get user info
var https = require('https');
var url = require('url');

var OAuth2Strategy = require('passport-oauth').OAuth2Strategy.Strategy;

module.exports = function(passport) {

    var oauth2 = new OAuth2Strategy(config.services.cern,
        function(accessToken, refreshToken, profile, done){
            var URI = url.parse(config.services.cern.infoURL);
            var user_req = https.request({
                host: URI.hostname,
                path: URI.pathname,
                port: URI.port,
                method: 'GET',
                headers: {
                    Authorization: "Bearer "+accessToken
                }
            }, function(res){
                var body = '';
                res.on('data', function(chunk){ body+= chunk; });
                res.on('end', function(){
                    var profile = JSON.parse(body);
                    console.log('Profile: %j', profile);
                    return done(null, profile);
                });
            }).on('error', function(e){
                console.log("Got an error: ", e);
                return done(e);
            }).end();
        }
    );

    passport.use(oauth2);

    passport.serializeUser(function(profile, done){
        done(null, profile);
    });

    passport.deserializeUser(function(profile, done){
        done(null, profile);
    });

};
<|endoftext|>


<commit_before>// @flow

const { clone, set } = require('lodash')
const fs = require('fs')
const Multihash = require('multihashes')
const { JQ_PATH } = require('../../metadata/jqStream')
const childProcess = require('child_process')
const sshTunnel = require('tunnel-ssh')
const { RestClient } = require('../api')
import type { Writable } from 'stream'
import type { WriteStream } from 'tty'

function formatJSON (obj: ?mixed,
                    options: {color?: ?boolean, pretty?: boolean} = {}): string {
  const compactOutput = options.pretty === false

  let useColor = false
  // print in color if explicitly enabled, or if pretty-printing to a tty
  if (options.color === true || (options.color == null && process.stdout.isTTY && !compactOutput)) {
    useColor = true
  }

  if (!useColor && compactOutput) {
    // skip jq if we don't want color or pretty printing
    return JSON.stringify(obj) + '\n'
  }

  const jqOpts = [(useColor ? '-C' : '-M'), '-a', '.']
  if (options.pretty === false) {
    jqOpts.unshift('-c')
  }

  return childProcess.execFileSync(JQ_PATH, jqOpts, {input: JSON.stringify(obj), encoding: 'utf-8'}).toString()
}

function printJSON (obj: ?mixed,
                    options: {color?: ?boolean, pretty?: boolean} = {}) {
  process.stdout.write(formatJSON(obj, options))
}

function pluralizeCount (count: number, word: string): string {
  let plural = word
  if (count !== 1) plural += 's'
  return count.toString() + ' ' + plural
}

function isB58Multihash (str: string): boolean {
  try {
    Multihash.fromB58String(str)
    return true
  } catch (err) {
    return false
  }
}

function setupSSHTunnel (config: Object): Promise<Object> {
  return new Promise((resolve, reject) => {
    sshTunnel(config, (err, server) => {
      if (err) return reject(err)
      resolve(server)
    })
  })
}

function ensureAll (obj: Object, keys: Array<string>, description: string = 'object') {
  for (const key of keys) {
    if (obj[key] === undefined) {
      throw new Error(`${description} is missing required field ${key}`)
    }
  }
}

function ensureAny (obj: Object, keys: Array<string>, description: string = 'object') {
  for (const key of keys) {
    if (obj[key] !== undefined) return
  }
  throw new Error(`${description} must have one of the following fields: ${keys.join(', ')}`)
}

function prepareSSHConfig (config: Object | string): Object {
  if (typeof config === 'string') {
    config = JSON.parse(fs.readFileSync(config, 'utf8'))
  }

  ensureAll(config, ['host', 'username'], 'SSH configuration')
  ensureAny(config, ['password', 'privateKey'], 'SSH configuration')

  const defaultOpts = {
    dstPort: '9002',
    localHost: 'localhost',
    keepAlive: true
  }

  return Object.assign({}, defaultOpts, config)
}

type GlobalOptions = {
  apiUrl: string,
  sshConfig?: string | Object,
  timeout: number
}

type SubcommandGlobalOptions = { // eslint-disable-line no-unused-vars
  client: RestClient
}

function subcommand<T: SubcommandGlobalOptions> (handler: (argv: T) => Promise<*>): (argv: GlobalOptions) => void {
  return (argv: GlobalOptions) => {
    const {apiUrl, sshConfig, timeout} = argv
    const client = new RestClient({apiUrl, requestTimeout: timeout})

    const sshTunnelConfig = (sshConfig != null)
      ? prepareSSHConfig(sshConfig)
      : null

    let sshTunnelPromise
    let sshTunnel = null
    if (sshTunnelConfig != null) {
      sshTunnelPromise = setupSSHTunnel(sshTunnelConfig)
        .then(tunnel => { sshTunnel = tunnel })
    } else {
      sshTunnelPromise = Promise.resolve()
    }

    function closeTunnel () {
      if (sshTunnel != null) {
        sshTunnel.close()
      }
    }

    // Using lodash as a kind of flow "escape valve", since it's being stubborn
    const subcommandOptions: T = set(clone(argv), 'client', client)

    sshTunnelPromise
      .then(() => handler(subcommandOptions))
      .then(closeTunnel)
      .catch(err => {
        closeTunnel()
        console.error(err.message)
        process.exit(1)
      })
  }
}

/**
 * Print `output` to the `destination` stream and append a newline.
 * @param output
 * @param destination
 */
function writeln (output: string, destination: Writable | WriteStream) {
  destination.write(output + '\n')
}

/**
 * Print `output` to stdout and append a newline.
 * Always use this instead of console.log for non-debug output!
 * console.log keeps a strong reference to whatever you pass in,
 * which can result in memory leaks for long-running processes.
 * @param output
 */
function println (output: string) {
  writeln(output, process.stdout)
}

/**
 * Print `output` to stderr and append a newline.
 * Use if you don't want console.error to keep a strong reference
 * to whatever you pass in.
 * @param output
 */
function printlnErr (output: string) {
  writeln(output, process.stderr)
}

module.exports = {
  println,
  printlnErr,
  formatJSON,
  printJSON,
  pluralizeCount,
  isB58Multihash,
  subcommand,
  prepareSSHConfig
}
<commit_msg>bind ssh tunnel to automatic port, rewrite api url<commit_after>// @flow

const { clone, set } = require('lodash')
const fs = require('fs')
const Multihash = require('multihashes')
const { JQ_PATH } = require('../../metadata/jqStream')
const childProcess = require('child_process')
const sshTunnel = require('tunnel-ssh')
const { RestClient } = require('../api')
import type { Writable } from 'stream'
import type { WriteStream } from 'tty'

function formatJSON (obj: ?mixed,
                    options: {color?: ?boolean, pretty?: boolean} = {}): string {
  const compactOutput = options.pretty === false

  let useColor = false
  // print in color if explicitly enabled, or if pretty-printing to a tty
  if (options.color === true || (options.color == null && process.stdout.isTTY && !compactOutput)) {
    useColor = true
  }

  if (!useColor && compactOutput) {
    // skip jq if we don't want color or pretty printing
    return JSON.stringify(obj) + '\n'
  }

  const jqOpts = [(useColor ? '-C' : '-M'), '-a', '.']
  if (options.pretty === false) {
    jqOpts.unshift('-c')
  }

  return childProcess.execFileSync(JQ_PATH, jqOpts, {input: JSON.stringify(obj), encoding: 'utf-8'}).toString()
}

function printJSON (obj: ?mixed,
                    options: {color?: ?boolean, pretty?: boolean} = {}) {
  process.stdout.write(formatJSON(obj, options))
}

function pluralizeCount (count: number, word: string): string {
  let plural = word
  if (count !== 1) plural += 's'
  return count.toString() + ' ' + plural
}

function isB58Multihash (str: string): boolean {
  try {
    Multihash.fromB58String(str)
    return true
  } catch (err) {
    return false
  }
}

function setupSSHTunnel (config: Object): Promise<Object> {
  return new Promise((resolve, reject) => {
    sshTunnel(config, (err, server) => {
      if (err) return reject(err)
      resolve(server)
    })
  })
}

function ensureAll (obj: Object, keys: Array<string>, description: string = 'object') {
  for (const key of keys) {
    if (obj[key] === undefined) {
      throw new Error(`${description} is missing required field ${key}`)
    }
  }
}

function ensureAny (obj: Object, keys: Array<string>, description: string = 'object') {
  for (const key of keys) {
    if (obj[key] !== undefined) return
  }
  throw new Error(`${description} must have one of the following fields: ${keys.join(', ')}`)
}

function prepareSSHConfig (config: Object | string): Object {
  if (typeof config === 'string') {
    config = JSON.parse(fs.readFileSync(config, 'utf8'))
  }

  ensureAll(config, ['host', 'username'], 'SSH configuration')
  ensureAny(config, ['password', 'privateKey'], 'SSH configuration')

  const defaultOpts = {
    dstPort: 9002,
    localPort: 0,
    localHost: 'localhost',
    keepAlive: true
  }

  return Object.assign({}, defaultOpts, config)
}

type GlobalOptions = {
  apiUrl: string,
  sshConfig?: string | Object,
  timeout: number
}

type SubcommandGlobalOptions = { // eslint-disable-line no-unused-vars
  client: RestClient
}

function subcommand<T: SubcommandGlobalOptions> (handler: (argv: T) => Promise<*>): (argv: GlobalOptions) => void {
  return (argv: GlobalOptions) => {
    const {sshConfig, timeout} = argv
    let {apiUrl} = argv

    const sshTunnelConfig = (sshConfig != null)
      ? prepareSSHConfig(sshConfig)
      : null

    let sshTunnelPromise
    let sshTunnel = null
    if (sshTunnelConfig != null) {
      sshTunnelPromise = setupSSHTunnel(sshTunnelConfig)
        .then(tunnel => {
          sshTunnel = tunnel
          const addr = sshTunnel.address()
          apiUrl = `http://${addr.address}:${addr.port}`
        })
    } else {
      sshTunnelPromise = Promise.resolve()
    }

    function closeTunnel () {
      if (sshTunnel != null) {
        sshTunnel.close()
      }
    }

    sshTunnelPromise
      .then(() => {
        const client = new RestClient({apiUrl, requestTimeout: timeout})
        return set(clone(argv), 'client', client)
      })
      .then(subcommandOptions => handler(subcommandOptions))
      .then(closeTunnel)
      .catch(err => {
        closeTunnel()
        console.error(err.message)
        process.exit(1)
      })
  }
}

/**
 * Print `output` to the `destination` stream and append a newline.
 * @param output
 * @param destination
 */
function writeln (output: string, destination: Writable | WriteStream) {
  destination.write(output + '\n')
}

/**
 * Print `output` to stdout and append a newline.
 * Always use this instead of console.log for non-debug output!
 * console.log keeps a strong reference to whatever you pass in,
 * which can result in memory leaks for long-running processes.
 * @param output
 */
function println (output: string) {
  writeln(output, process.stdout)
}

/**
 * Print `output` to stderr and append a newline.
 * Use if you don't want console.error to keep a strong reference
 * to whatever you pass in.
 * @param output
 */
function printlnErr (output: string) {
  writeln(output, process.stderr)
}

module.exports = {
  println,
  printlnErr,
  formatJSON,
  printJSON,
  pluralizeCount,
  isB58Multihash,
  subcommand,
  prepareSSHConfig
}
<|endoftext|>


<commit_before><commit_msg>Update spect.js<commit_after><|endoftext|>


<commit_before>$(function() {

  $("#contactForm input,#contactForm textarea").jqBootstrapValidation({
    preventSubmit: true,
    submitError: function($form, event, errors) {
      // additional error messages or events
      console.log("form submit error: "+errors)
    },
    submitSuccess: function($form, event) {
      console.log("form submit begin")
      console.log("fields: "+$form.serialize())
      event.preventDefault(); // prevent default submit behaviour
      // get values from FORM
      var name = $("input#name").val();
      var email = $("input#email").val();
      var phone = $("input#phone").val();
      var message = $("textarea#message").val();
      var firstName = name; // For Success/Failure Message
      // Check for white space in name for Success/Fail message
      if (firstName.indexOf(' ') >= 0) {
        firstName = name.split(' ').slice(0, -1).join(' ');
      }
      $this = $("#sendMessageButton");
      $this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages
      $.ajax({
        url: $form.attr("action"),
        type: "POST",
        data: $form.serialize(),
        cache: false,
        success: function() {
          console.log("form submit success")
          // Success message
          $('#success').html("
");
          $('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
            .append("</button>");
          $('#success > .alert-success')
            .append("Your message has been sent. ");
          $('#success > .alert-success')
            .append('
');
          //clear all fields
          $('#contactForm').trigger("reset");
        },
        error: function() {
          console.log("form submit error")
          // Fail message
          $('#success').html("
");
          $('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
            .append("</button>");
          $('#success > .alert-danger').append($("").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
          $('#success > .alert-danger').append('
');
          //clear all fields
          $('#contactForm').trigger("reset");
        },
        complete: function() {
          setTimeout(function() {
            $this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
          }, 1000);
        }
      });
    },
    filter: function() {
      return $(this).is(":visible");
    },
  });

  $("a[data-toggle=\"tab\"]").click(function(e) {
    e.preventDefault();
    $(this).tab("show");
  });
});

/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
  $('#success').html('');
});
<commit_msg>add form check before submitting<commit_after>$(function() {

  $("#contactForm input,#contactForm textarea").jqBootstrapValidation({
    preventSubmit: true,
    submitError: function($form, event, errors) {
      // additional error messages or events
      console.log("form submit error: "+errors)
    },
    submitSuccess: function($form, event) {
      console.log("form submit begin")
      console.log("fields: "+$form.serialize())
      event.preventDefault(); // prevent default submit behaviour
      // get values from FORM
      var name = $("input#name").val();
      var email = $("input#email").val();
      var phone = $("input#phone").val();
      var message = $("textarea#message").val();
      var firstName = name; // For Success/Failure Message
      // Check for white space in name for Success/Fail message
      if (firstName.indexOf(' ') >= 0) {
        firstName = name.split(' ').slice(0, -1).join(' ');
      }
      $this = $("#sendMessageButton");
      $this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages

      if( !name || !email || !phone || !message ) {
        return
      }

      $.ajax({
        url: $form.attr("action"),
        type: "POST",
        data: $form.serialize(),
        cache: false,
        success: function() {
          console.log("form submit success")
          // Success message
          $('#success').html("
");
          $('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
            .append("</button>");
          $('#success > .alert-success')
            .append("Your message has been sent. ");
          $('#success > .alert-success')
            .append('
');
          //clear all fields
          $('#contactForm').trigger("reset");
        },
        error: function() {
          console.log("form submit error")
          // Fail message
          $('#success').html("
");
          $('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
            .append("</button>");
          $('#success > .alert-danger').append($("").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
          $('#success > .alert-danger').append('
');
          //clear all fields
          $('#contactForm').trigger("reset");
        },
        complete: function() {
          setTimeout(function() {
            $this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
          }, 1000);
        }
      });
    },
    filter: function() {
      return $(this).is(":visible");
    },
  });

  $("a[data-toggle=\"tab\"]").click(function(e) {
    e.preventDefault();
    $(this).tab("show");
  });
});

/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
  $('#success').html('');
});
<|endoftext|>