<commit_before>'use strict';

var _ = require('lodash');
var $ = require('preconditions').singleton();
var sjcl = require('sjcl');

var Bitcore = require('bitcore');
var Address = Bitcore.Address;
var PrivateKey = Bitcore.PrivateKey;
var PublicKey = Bitcore.PublicKey;
var crypto = Bitcore.crypto;
var encoding = Bitcore.encoding;

function WalletUtils() {};

WalletUtils.PATHS = {
  BASE_ADDRESS_DERIVATION: "m/45'",
  REQUEST_KEY: "m/1'/0",
  TXPROPOSAL_KEY: "m/1'/1",
};

/* TODO: It would be nice to be compatible with bitcoind signmessage. How
 * the hash is calculated there? */
WalletUtils.hashMessage = function(text) {
  $.checkArgument(text);
  var buf = new Buffer(text);
  var ret = crypto.Hash.sha256sha256(buf);
  ret = new Bitcore.encoding.BufferReader(ret).readReverse();
  return ret;
};

WalletUtils.signMessage = function(text, privKey) {
  $.checkArgument(text);
  var priv = new PrivateKey(privKey);
  var hash = WalletUtils.hashMessage(text);
  return crypto.ECDSA.sign(hash, priv, 'little').toString();
};

WalletUtils.verifyMessage = function(text, signature, pubKey) {
  $.checkArgument(text);
  $.checkArgument(pubKey);

  if (!signature)
    return false;

  var pub = new PublicKey(pubKey);
  var hash = WalletUtils.hashMessage(text);

  try {
    var sig = new crypto.Signature.fromString(signature);
    return crypto.ECDSA.verify(hash, sig, pub, 'little');
  } catch (e) {
    return false;
  }
};

WalletUtils.deriveAddress = function(publicKeyRing, path, m, network) {
  var publicKeys = _.map(publicKeyRing, function(item) {
    var xpub = new Bitcore.HDPublicKey(item.xPubKey);
    return xpub.derive(path).publicKey;
  });

  var bitcoreAddress = Address.createMultisig(publicKeys, m, network);

  return {
    address: bitcoreAddress.toString(),
    path: path,
    publicKeys: _.invoke(publicKeys, 'toString'),
  };
};

WalletUtils.getProposalHash = function(toAddress, amount, message) {
  return [toAddress, amount, (message || '')].join('|');
};

WalletUtils.getCopayerHash = function(name, xPubKey, requestPubKey) {
  return [name, xPubKey, requestPubKey].join('|');
};

WalletUtils.xPubToCopayerId = function(xpub) {
  var hash = sjcl.hash.sha256.hash(xpub);
  return sjcl.codec.hex.fromBits(hash);
};

WalletUtils.toSecret = function(walletId, walletPrivKey, network) {
  if (_.isString(walletPrivKey)) {
    walletPrivKey = Bitcore.PrivateKey.fromString(walletPrivKey);
  }
  var widHex = new Buffer(walletId.replace(/-/g, ''), 'hex');
  var widBase58 = new encoding.Base58(widHex).toString();
  return _.padRight(widBase58, 22, '0') + walletPrivKey.toWIF() + (network == 'testnet' ? 'T' : 'L');
};

WalletUtils.fromSecret = function(secret) {
  $.checkArgument(secret);

  function split(str, indexes) {
    var parts = [];
    indexes.push(str.length);
    var i = 0;
    while (i < indexes.length) {
      parts.push(str.substring(i == 0 ? 0 : indexes[i - 1], indexes[i]));
      i++;
    };
    return parts;
  };

  try {
    var secretSplit = split(secret, [22, 74]);
    var widBase58 = secretSplit[0].replace(/0/g, '');
    var widHex = encoding.Base58.decode(widBase58).toString('hex');
    var walletId = split(widHex, [8, 12, 16, 20]).join('-');

    var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
    var networkChar = secretSplit[2];

    return {
      walletId: walletId,
      walletPrivKey: walletPrivKey,
      network: networkChar == 'T' ? 'testnet' : 'livenet',
    };
  } catch (ex) {
    throw new Error('Invalid secret');
  }
};

WalletUtils.encryptMessage = function(message, encryptingKey) {
  var key = sjcl.codec.base64.toBits(encryptingKey);
  return sjcl.encrypt(key, message, {
    ks: 128,
    iter: 1
  });
};

WalletUtils.decryptMessage = function(cyphertextJson, encryptingKey) {
  var key = sjcl.codec.base64.toBits(encryptingKey);
  return sjcl.decrypt(key, cyphertextJson);
};

WalletUtils.privateKeyToAESKey = function(privKey) {
  var pk = Bitcore.PrivateKey.fromString(privKey);
  return Bitcore.crypto.Hash.sha256(pk.toBuffer()).slice(0, 16).toString('base64');
};

WalletUtils.buildTx = function(txp) {
  var t = new Bitcore.Transaction();

  _.each(txp.inputs, function(i) {
    t.from(i, i.publicKeys, txp.requiredSignatures);
  });

  t.to(txp.toAddress, txp.amount)
    .change(txp.changeAddress.address);

  // Shuffle outputs for improved privacy
  t.sortOutputs(function(outputs) {
    return _.map(txp.outputOrder, function(i) {
      return outputs[i];
    });
  });

  return t;
};

WalletUtils.signTxp = function(txp, xPrivKey) {
  var self = this;

  //Derive proper key to sign, for each input
  var privs = [],
    derived = {};

  var network = new Bitcore.Address(txp.toAddress).network.name;
  var xpriv = new Bitcore.HDPrivateKey(xPrivKey, network).derive(WalletUtils.PATHS.BASE_ADDRESS_DERIVATION);

  _.each(txp.inputs, function(i) {
    if (!derived[i.path]) {
      derived[i.path] = xpriv.derive(i.path).privateKey;
      privs.push(derived[i.path]);
    }
  });

  var t = WalletUtils.buildTx(txp);

  var signatures = _.map(privs, function(priv, i) {
    return t.getSignatures(priv);
  });

  signatures = _.map(_.sortBy(_.flatten(signatures), 'inputIndex'), function(s) {
    return s.signature.toDER().toString('hex');
  });

  return signatures;
};

WalletUtils.getNetworkFromXPubKey = function(xPubKey) {
  return xPubKey.substr(0, 4) == 'tpub' ? 'testnet' : 'livenet';
};

module.exports = WalletUtils;
<commit_msg>shuffle ouputs only when > 1<commit_after>'use strict';

var _ = require('lodash');
var $ = require('preconditions').singleton();
var sjcl = require('sjcl');

var Bitcore = require('bitcore');
var Address = Bitcore.Address;
var PrivateKey = Bitcore.PrivateKey;
var PublicKey = Bitcore.PublicKey;
var crypto = Bitcore.crypto;
var encoding = Bitcore.encoding;

function WalletUtils() {};

WalletUtils.PATHS = {
  BASE_ADDRESS_DERIVATION: "m/45'",
  REQUEST_KEY: "m/1'/0",
  TXPROPOSAL_KEY: "m/1'/1",
};

/* TODO: It would be nice to be compatible with bitcoind signmessage. How
 * the hash is calculated there? */
WalletUtils.hashMessage = function(text) {
  $.checkArgument(text);
  var buf = new Buffer(text);
  var ret = crypto.Hash.sha256sha256(buf);
  ret = new Bitcore.encoding.BufferReader(ret).readReverse();
  return ret;
};

WalletUtils.signMessage = function(text, privKey) {
  $.checkArgument(text);
  var priv = new PrivateKey(privKey);
  var hash = WalletUtils.hashMessage(text);
  return crypto.ECDSA.sign(hash, priv, 'little').toString();
};

WalletUtils.verifyMessage = function(text, signature, pubKey) {
  $.checkArgument(text);
  $.checkArgument(pubKey);

  if (!signature)
    return false;

  var pub = new PublicKey(pubKey);
  var hash = WalletUtils.hashMessage(text);

  try {
    var sig = new crypto.Signature.fromString(signature);
    return crypto.ECDSA.verify(hash, sig, pub, 'little');
  } catch (e) {
    return false;
  }
};

WalletUtils.deriveAddress = function(publicKeyRing, path, m, network) {
  var publicKeys = _.map(publicKeyRing, function(item) {
    var xpub = new Bitcore.HDPublicKey(item.xPubKey);
    return xpub.derive(path).publicKey;
  });

  var bitcoreAddress = Address.createMultisig(publicKeys, m, network);

  return {
    address: bitcoreAddress.toString(),
    path: path,
    publicKeys: _.invoke(publicKeys, 'toString'),
  };
};

WalletUtils.getProposalHash = function(toAddress, amount, message) {
  return [toAddress, amount, (message || '')].join('|');
};

WalletUtils.getCopayerHash = function(name, xPubKey, requestPubKey) {
  return [name, xPubKey, requestPubKey].join('|');
};

WalletUtils.xPubToCopayerId = function(xpub) {
  var hash = sjcl.hash.sha256.hash(xpub);
  return sjcl.codec.hex.fromBits(hash);
};

WalletUtils.toSecret = function(walletId, walletPrivKey, network) {
  if (_.isString(walletPrivKey)) {
    walletPrivKey = Bitcore.PrivateKey.fromString(walletPrivKey);
  }
  var widHex = new Buffer(walletId.replace(/-/g, ''), 'hex');
  var widBase58 = new encoding.Base58(widHex).toString();
  return _.padRight(widBase58, 22, '0') + walletPrivKey.toWIF() + (network == 'testnet' ? 'T' : 'L');
};

WalletUtils.fromSecret = function(secret) {
  $.checkArgument(secret);

  function split(str, indexes) {
    var parts = [];
    indexes.push(str.length);
    var i = 0;
    while (i < indexes.length) {
      parts.push(str.substring(i == 0 ? 0 : indexes[i - 1], indexes[i]));
      i++;
    };
    return parts;
  };

  try {
    var secretSplit = split(secret, [22, 74]);
    var widBase58 = secretSplit[0].replace(/0/g, '');
    var widHex = encoding.Base58.decode(widBase58).toString('hex');
    var walletId = split(widHex, [8, 12, 16, 20]).join('-');

    var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
    var networkChar = secretSplit[2];

    return {
      walletId: walletId,
      walletPrivKey: walletPrivKey,
      network: networkChar == 'T' ? 'testnet' : 'livenet',
    };
  } catch (ex) {
    throw new Error('Invalid secret');
  }
};

WalletUtils.encryptMessage = function(message, encryptingKey) {
  var key = sjcl.codec.base64.toBits(encryptingKey);
  return sjcl.encrypt(key, message, {
    ks: 128,
    iter: 1
  });
};

WalletUtils.decryptMessage = function(cyphertextJson, encryptingKey) {
  var key = sjcl.codec.base64.toBits(encryptingKey);
  return sjcl.decrypt(key, cyphertextJson);
};

WalletUtils.privateKeyToAESKey = function(privKey) {
  var pk = Bitcore.PrivateKey.fromString(privKey);
  return Bitcore.crypto.Hash.sha256(pk.toBuffer()).slice(0, 16).toString('base64');
};

WalletUtils.buildTx = function(txp) {
  var t = new Bitcore.Transaction();

  _.each(txp.inputs, function(i) {
    t.from(i, i.publicKeys, txp.requiredSignatures);
  });

  t.to(txp.toAddress, txp.amount)
    .change(txp.changeAddress.address);

  // Shuffle outputs for improved privacy
  if (t.outputs.length > 1) {
    $.checkState(t.outputs.length == txp.outputOrder.length);
    t.sortOutputs(function(outputs) {
      return _.map(txp.outputOrder, function(i) {
        return outputs[i];
      });
    });
  }

  return t;
};

WalletUtils.signTxp = function(txp, xPrivKey) {
  var self = this;

  //Derive proper key to sign, for each input
  var privs = [],
    derived = {};

  var network = new Bitcore.Address(txp.toAddress).network.name;
  var xpriv = new Bitcore.HDPrivateKey(xPrivKey, network).derive(WalletUtils.PATHS.BASE_ADDRESS_DERIVATION);

  _.each(txp.inputs, function(i) {
    if (!derived[i.path]) {
      derived[i.path] = xpriv.derive(i.path).privateKey;
      privs.push(derived[i.path]);
    }
  });

  var t = WalletUtils.buildTx(txp);

  var signatures = _.map(privs, function(priv, i) {
    return t.getSignatures(priv);
  });

  signatures = _.map(_.sortBy(_.flatten(signatures), 'inputIndex'), function(s) {
    return s.signature.toDER().toString('hex');
  });

  return signatures;
};

WalletUtils.getNetworkFromXPubKey = function(xPubKey) {
  return xPubKey.substr(0, 4) == 'tpub' ? 'testnet' : 'livenet';
};

module.exports = WalletUtils;
<|endoftext|>


<commit_before>// Find first index of array

'use strict';

var assertNotNull = require('../../assert-not-null');

module.exports = function () {
	var i, l;
	assertNotNull(this);
	if (!(l = (this.length >>> 0))) {
		return null;
	}
	i = 0;
	while (!this.hasOwnProperty(i)) {
		if (i++ === l) {
			return null;
		}
	}
	return i;
};
<commit_msg>Loop should be exited index earlier<commit_after>// Find first index of array

'use strict';

var assertNotNull = require('../../assert-not-null');

module.exports = function () {
	var i, l;
	assertNotNull(this);
	if (!(l = (this.length >>> 0))) {
		return null;
	}
	i = 0;
	while (!this.hasOwnProperty(i)) {
		if (++i === l) {
			return null;
		}
	}
	return i;
};
<|endoftext|>


<commit_before>/* eslint-disable */

// number of experiences per country
db.getCollection('experiences').aggregate([
    {
        $group: {
            _id: { country : "$country" },
            count : { $sum : 1 }
        }
    }
]);

// get just the countries through experiences
db.getCollection('experiences').aggregate([
    {
        $group : {
            _id : { country : "$country" }
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "_id.country",
            foreignField : "_id",
            as: "country"
        }
    },
    {
        $project : { _id : false }
    },
    {
        $unwind : "$country"
    }
]);

// gets countries and their average ratings
db.getCollection('experiences').aggregate([
    {
        $project : {
            _id: false,
            country : true,
            rating : true
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $group : {
            _id : "$country.name",
            avg_rating : { $avg : "$rating" }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets countries with average ratings and number of experiences
db.getCollection('experiences').aggregate([
    {
        $project : {
            _id: false,
            country : true,
            rating : true
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $group : {
            _id : "$country.name",
            avg_rating : { $avg : "$rating" },
            num_experiences : { $sum : 1 }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets each country by name and an array of all travelers who have visited
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $lookup : {
            from : "users",
            localField : "user",
            foreignField : "_id",
            as : "user"
        }
    },
    {
        $project : {
            country : true,
            user : true,
            _id : false
        }
    },
    {
        $group : {
            _id : "$country.name",
            travelers : { $push : "$user.name" }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets all spanish speaking countries and sorts them by ranking
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : 'countries',
            localField : 'country',
            foreignField : '_id',
            as : 'country'
        }
    },
    {
        $lookup : {
            from : 'users',
            localField : 'user',
            foreignField : '_id',
            as : 'user'
        }
    },
    {

        $project : {
            rating : true,
            country : true,
            _id : false
        }
    },
    {
        $match : {
            'country.languages' : 'Spanish'
        }
    },
    {
        $group : {
            _id : '$country.name',
            rating : { $avg : '$rating' }
        }
    },
    {
        $sort : {
            rating : -1
        }
    },
]);

// gets the diff between gender literacy rate
db.getCollection('countries').aggregate(
    {
        $project: {
            _id: false,
            "name":1,
            "literacy":1,
            "literacyGap": {
                $subtract: [
                    "$literacy.male",
                    "$literacy.female"
                ]
            }
        }
    },
    {
        $sort: { "literacyGap": 1 }
    },
    {
        $match: {
            "literacyGap": { $ne: null }
        }
    }
    );

    //gets highest levels of child labor
    db.getCollection('countries').aggregate([{

               $project: {
                _id: false,
                "name":1,
                "child_labor.percentage":1,

            }

        },
        {
            $sort: { "child_labor.percentage": 1 }
        },
        {
                $match: {
                    "child_labor.percentage": { $ne: null }
                }
          }

    ])

    //by percentage of internet users

    db.getCollection('countries').aggregate([
        {
              $match: {
                  "internet_users.percentage": {
                        $lt: 20,
                        $ne: null
                   }
              }
        },

        {
            $sort: { "internet_users.percentage": 1 }
        },

        {
            $project: {
                _id: false,
                name: 1,
                "percentage of internet users": "$internet_users.percentage"

         }

        }
);

// gets all languages paired with an array of which countries speak them
// languages are sorted by number of countries they are spoken in
// countries per language are sorted by user rating
db.getCollection('experiences').aggregate([
        {
            $lookup : {
                from : 'countries',
                localField : 'country',
                foreignField : '_id',
                as : 'country'
            }
        },
        {
            $lookup : {
                from : 'users',
                localField : 'user',
                foreignField : '_id',
                as : 'user'
            }
        },
        {

            $project : {
                rating : true,
                country : '$country.name',
                languages : '$country.languages.all',
                _id : false
            }
        },
        {
            $project : { 'languages.percentage': false }
        },
        {
            $unwind : '$languages'
        },
        {

            $unwind : '$languages'
        },
        {
            $project : {
                rating : true,
                country : true,
                language : '$languages.language'
            }
        },
        {
            $unwind : '$country'
        },
        {
            $group : {
                _id : { country : '$country', language : '$language' },
                avg_rating : { $avg : '$rating' }
            }
        },
        {
            $sort : { avg_rating : -1 }
        },
        {
            $group : {
                _id : { language : '$_id.language' },
                countries : {
                    $push : {
                        country : '$_id.country',
                        avg_rating : '$avg_rating'
                    }
                }
            }
        },
        {
            $project : {
                _id : false,
                language : '$_id.language',
                countries : '$countries',
                num : { $size : '$countries' }
            }
        },
        {
            $sort : { num : -1 }
        }
]);
    {
        $match : { 'language' : ${queryInput} }
    }

// user's list of countries sorted by trip date
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : 'countries',
            localField : 'country',
            foreignField : '_id',
            as : 'country'
        }
    },
    {
         $lookup : {
             from : 'users',
             localField : 'user',
             foreignField : '_id',
             as : 'user'
         }
    },
    {
        $unwind : '$user'
    },
    {
        $unwind : '$country'
    },
    {
        $sort : { 'createdAt' : -1 }
    },
    {
        $group : {
            _id : '$user.name',
            countries : {
                $push : {
                    country : '$country.name',
                    city : '$city',
                    date : '$createdAt'
                }
            }
        }
    },
    {
        $project : {
            _id : false,
            user : '$_id',
            count : { $size : '$countries' },
            countries : true
        }
    }
]);
    {
        $match : { 'user' : ${queryInput} }
    }
<commit_msg>comment out match templates<commit_after>/* eslint-disable */

// number of experiences per country
db.getCollection('experiences').aggregate([
    {
        $group: {
            _id: { country : "$country" },
            count : { $sum : 1 }
        }
    }
]);

// get just the countries through experiences
db.getCollection('experiences').aggregate([
    {
        $group : {
            _id : { country : "$country" }
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "_id.country",
            foreignField : "_id",
            as: "country"
        }
    },
    {
        $project : { _id : false }
    },
    {
        $unwind : "$country"
    }
]);

// gets countries and their average ratings
db.getCollection('experiences').aggregate([
    {
        $project : {
            _id: false,
            country : true,
            rating : true
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $group : {
            _id : "$country.name",
            avg_rating : { $avg : "$rating" }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets countries with average ratings and number of experiences
db.getCollection('experiences').aggregate([
    {
        $project : {
            _id: false,
            country : true,
            rating : true
        }
    },
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $group : {
            _id : "$country.name",
            avg_rating : { $avg : "$rating" },
            num_experiences : { $sum : 1 }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets each country by name and an array of all travelers who have visited
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : "countries",
            localField : "country",
            foreignField : "_id",
            as : "country"
        }
    },
    {
        $lookup : {
            from : "users",
            localField : "user",
            foreignField : "_id",
            as : "user"
        }
    },
    {
        $project : {
            country : true,
            user : true,
            _id : false
        }
    },
    {
        $group : {
            _id : "$country.name",
            travelers : { $push : "$user.name" }
        }
    },
    {
        $unwind : "$_id"
    }
]);

// gets all spanish speaking countries and sorts them by ranking
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : 'countries',
            localField : 'country',
            foreignField : '_id',
            as : 'country'
        }
    },
    {
        $lookup : {
            from : 'users',
            localField : 'user',
            foreignField : '_id',
            as : 'user'
        }
    },
    {

        $project : {
            rating : true,
            country : true,
            _id : false
        }
    },
    {
        $match : {
            'country.languages' : 'Spanish'
        }
    },
    {
        $group : {
            _id : '$country.name',
            rating : { $avg : '$rating' }
        }
    },
    {
        $sort : {
            rating : -1
        }
    },
]);

// gets the diff between gender literacy rate
db.getCollection('countries').aggregate(
    {
        $project: {
            _id: false,
            "name":1,
            "literacy":1,
            "literacyGap": {
                $subtract: [
                    "$literacy.male",
                    "$literacy.female"
                ]
            }
        }
    },
    {
        $sort: { "literacyGap": 1 }
    },
    {
        $match: {
            "literacyGap": { $ne: null }
        }
    }
    );

    //gets highest levels of child labor
    db.getCollection('countries').aggregate([{

               $project: {
                _id: false,
                "name":1,
                "child_labor.percentage":1,

            }

        },
        {
            $sort: { "child_labor.percentage": 1 }
        },
        {
                $match: {
                    "child_labor.percentage": { $ne: null }
                }
          }

    ])

    //by percentage of internet users

    db.getCollection('countries').aggregate([
        {
              $match: {
                  "internet_users.percentage": {
                        $lt: 20,
                        $ne: null
                   }
              }
        },

        {
            $sort: { "internet_users.percentage": 1 }
        },

        {
            $project: {
                _id: false,
                name: 1,
                "percentage of internet users": "$internet_users.percentage"

         }

        }
    ]);

// gets all languages paired with an array of which countries speak them
// languages are sorted by number of countries they are spoken in
// countries per language are sorted by user rating
db.getCollection('experiences').aggregate([
        {
            $lookup : {
                from : 'countries',
                localField : 'country',
                foreignField : '_id',
                as : 'country'
            }
        },
        {
            $lookup : {
                from : 'users',
                localField : 'user',
                foreignField : '_id',
                as : 'user'
            }
        },
        {

            $project : {
                rating : true,
                country : '$country.name',
                languages : '$country.languages.all',
                _id : false
            }
        },
        {
            $project : { 'languages.percentage': false }
        },
        {
            $unwind : '$languages'
        },
        {

            $unwind : '$languages'
        },
        {
            $project : {
                rating : true,
                country : true,
                language : '$languages.language'
            }
        },
        {
            $unwind : '$country'
        },
        {
            $group : {
                _id : { country : '$country', language : '$language' },
                avg_rating : { $avg : '$rating' }
            }
        },
        {
            $sort : { avg_rating : -1 }
        },
        {
            $group : {
                _id : { language : '$_id.language' },
                countries : {
                    $push : {
                        country : '$_id.country',
                        avg_rating : '$avg_rating'
                    }
                }
            }
        },
        {
            $project : {
                _id : false,
                language : '$_id.language',
                countries : '$countries',
                num : { $size : '$countries' }
            }
        },
        {
            $sort : { num : -1 }
        }
]);
    // {
    //     $match : { 'language' : ${queryInput} }
    // }

// user's list of countries sorted by trip date
db.getCollection('experiences').aggregate([
    {
        $lookup : {
            from : 'countries',
            localField : 'country',
            foreignField : '_id',
            as : 'country'
        }
    },
    {
         $lookup : {
             from : 'users',
             localField : 'user',
             foreignField : '_id',
             as : 'user'
         }
    },
    {
        $unwind : '$user'
    },
    {
        $unwind : '$country'
    },
    {
        $sort : { 'createdAt' : -1 }
    },
    {
        $group : {
            _id : '$user.name',
            countries : {
                $push : {
                    country : '$country.name',
                    city : '$city',
                    date : '$createdAt'
                }
            }
        }
    },
    {
        $project : {
            _id : false,
            user : '$_id',
            count : { $size : '$countries' },
            countries : true
        }
    }
]);
    // {
    //     $match : { 'user' : ${queryInput} }
    // }
<|endoftext|>


<commit_before>(function () {

	"use strict";

	var app = angular.module('RbsChange'),
		modelIcons = {
			//'Rbs_Website_Website' : 'icon-home'
		};

	app.directive('breadcrumb', ['$location', 'RbsChange.Utils', 'RbsChange.i18n', function ($location, Utils, i18n) {

		return {
			restrict : 'E',

			scope : true,

			template : '

',

			replace : true,

			link : function (scope, elm) {

				function getLabelSuffix () {
					var search = $location.search(), page;
					if ('offset' in search && 'limit' in search) {
						page = 1 + (search.offset / search.limit);
						return ' — page ' + page + '';
					}
					return '';
				}

				function getEntryHtml (entry, disabled, last, cssClass) {

					var item, html, icon;

					if (Utils.isDocument(entry)) {
						item = [ entry.label ];
						if (entry.treeUrl()) {
							item.push(entry.treeUrl());
						} else {
							item.push("javascript:;");
						}
						if (modelIcons[entry.model]) {
							item.push(modelIcons[entry.model]);
						}
					} else if (angular.isString(entry)) {
						item = [ entry ];
					} else {
						item = entry;
					}

					icon = item.length === 3 && item[2] ? ' ' : '';

					if (disabled || last) {
						html = '
' + icon + item[0];
						if (last) {
							html += getLabelSuffix();
						}
						html += '
';
					} else {
						html = '
' + icon + item[0] + '
';
					}

					return html;
				}

				function update (breadcrumbData) {
					var html, i;
					html = getEntryHtml(
						[i18n.trans('m.rbs.admin.admin.js.home | ucf'), ""],
						breadcrumbData.disabled,
						// Last element?
						breadcrumbData.location.length === 0 && breadcrumbData.path.length === 0 && ! breadcrumbData.resource,
						'location'
					);

					for (i = 0; i < breadcrumbData.location.length; i++) {
						if (angular.isDefined(breadcrumbData.location[i])) {
							html += getEntryHtml(
								breadcrumbData.location[i],
								breadcrumbData.disabled,
								// Last element?
								i === (breadcrumbData.location.length-1) && breadcrumbData.path.length === 0 && ! breadcrumbData.resource,
								'location'
							);
						}
					}

					for (i = 0; i < breadcrumbData.path.length; i++) {
						if (angular.isDefined(breadcrumbData.path[i])) {
							html += getEntryHtml(
								breadcrumbData.path[i],
								false,
								// Last element?
								i === (breadcrumbData.path.length-1) && ! breadcrumbData.resource
							);
						}
					}

					if (breadcrumbData.resource) {
						html += getEntryHtml(breadcrumbData.resource, breadcrumbData.disabled, true);
					}

					if (breadcrumbData.resourceModifier) {
						html += getEntryHtml(breadcrumbData.resourceModifier, breadcrumbData.disabled, true);
					}

					elm.html(html);
				}

				scope.$on('Change:TreePathChanged', function (event, breadcrumbData) {
					update(breadcrumbData);
				});

			}

		};

	}]);

	app.directive('location', function () {
		return {
			restrict : 'E',
			require : '?^rbsBreadcrumbConfig',
			link : function (scope, element, attrs, rbsBreadcrumbConfig) {
				if (rbsBreadcrumbConfig) {
					attrs.$observe('href', function (href) {
						rbsBreadcrumbConfig.add('Location', element.index(), element.text(), href);
					});
				}
			}
		};
	});

	app.directive('rbsBreadcrumbConfig', ['RbsChange.Breadcrumb', function (Breadcrumb) {

		return {
			restrict : 'E',
			scope : true,
			require : '^rbsWorkspaceConfig',

			controller : ['$scope', function ($scope) {

				var bc = {
					'Location' : [],
					'Path' : []
				};

				this.add = function (type, index, text, href) {
					bc[type][index] = [text, href];
					if (bc[type].length === $scope.counts[type]) {
						Breadcrumb['set' + type](bc[type]);
					}
				};

			}],

			compile : function (tElement) {
				tElement.hide();
				return function linkFn (scope, element) {
					scope.counts = {
						'Location' : element.find('location').length,
						'Path' : element.find('path').length
					};
				};
			}
		};

	}]);

	app.directive('path', function () {
		return {
			restrict : 'E',
			require : '?^rbsBreadcrumbConfig',
			link : function (scope, element, attrs, rbsBreadcrumbConfig) {
				if (rbsBreadcrumbConfig) {
					attrs.$observe('href', function (href) {
						rbsBreadcrumbConfig.add('Path', element.index()-element.prevAll('location').length, element.text(), href);
					});
				}
			}
		};
	});

	app.directive('rbsWorkspaceConfig', ['RbsChange.Workspace', 'RbsChange.MainMenu', function (Workspace, MainMenu) {
		return {
			restrict : 'E',
			scope : true,
			controller : ['$scope', '$attrs', function ($scope, $attrs) {
				if ($attrs.menu) {
					MainMenu.loadModuleMenu($attrs.menu);
				}
			}]
		};

	}]);

})();<commit_msg>Changed Breadcrumb's events: 'Change:TreePathChanged' is only triggered when the 'path' changes, while 'Change:BreadcrumbChanged' is triggered every time something changes in the Breadcrumb. Changing the 'path' triggers both events.<commit_after>(function () {

	"use strict";

	var app = angular.module('RbsChange'),
		modelIcons = {
			//'Rbs_Website_Website' : 'icon-home'
		};

	app.directive('breadcrumb', ['$location', 'RbsChange.Utils', 'RbsChange.i18n', function ($location, Utils, i18n) {

		return {
			restrict : 'E',

			scope : true,

			template : '

',

			replace : true,

			link : function (scope, elm) {

				function getLabelSuffix () {
					var search = $location.search(), page;
					if ('offset' in search && 'limit' in search) {
						page = 1 + (search.offset / search.limit);
						return ' — page ' + page + '';
					}
					return '';
				}

				function getEntryHtml (entry, disabled, last, cssClass) {

					var item, html, icon;

					if (Utils.isDocument(entry)) {
						item = [ entry.label ];
						if (entry.treeUrl()) {
							item.push(entry.treeUrl());
						} else {
							item.push("javascript:;");
						}
						if (modelIcons[entry.model]) {
							item.push(modelIcons[entry.model]);
						}
					} else if (angular.isString(entry)) {
						item = [ entry ];
					} else {
						item = entry;
					}

					icon = item.length === 3 && item[2] ? ' ' : '';

					if (disabled || last) {
						html = '
' + icon + item[0];
						if (last) {
							html += getLabelSuffix();
						}
						html += '
';
					} else {
						html = '
' + icon + item[0] + '
';
					}

					return html;
				}

				function update (breadcrumbData) {
					var html, i;
					html = getEntryHtml(
						[i18n.trans('m.rbs.admin.admin.js.home | ucf'), ""],
						breadcrumbData.disabled,
						// Last element?
						breadcrumbData.location.length === 0 && breadcrumbData.path.length === 0 && ! breadcrumbData.resource,
						'location'
					);

					for (i = 0; i < breadcrumbData.location.length; i++) {
						if (angular.isDefined(breadcrumbData.location[i])) {
							html += getEntryHtml(
								breadcrumbData.location[i],
								breadcrumbData.disabled,
								// Last element?
								i === (breadcrumbData.location.length-1) && breadcrumbData.path.length === 0 && ! breadcrumbData.resource,
								'location'
							);
						}
					}

					for (i = 0; i < breadcrumbData.path.length; i++) {
						if (angular.isDefined(breadcrumbData.path[i])) {
							html += getEntryHtml(
								breadcrumbData.path[i],
								false,
								// Last element?
								i === (breadcrumbData.path.length-1) && ! breadcrumbData.resource
							);
						}
					}

					if (breadcrumbData.resource) {
						html += getEntryHtml(breadcrumbData.resource, breadcrumbData.disabled, true);
					}

					if (breadcrumbData.resourceModifier) {
						html += getEntryHtml(breadcrumbData.resourceModifier, breadcrumbData.disabled, true);
					}

					elm.html(html);
				}

				scope.$on('Change:BreadcrumbChanged', function (event, breadcrumbData) {
					update(breadcrumbData);
				});

			}

		};

	}]);

	app.directive('location', function () {
		return {
			restrict : 'E',
			require : '?^rbsBreadcrumbConfig',
			link : function (scope, element, attrs, rbsBreadcrumbConfig) {
				if (rbsBreadcrumbConfig) {
					attrs.$observe('href', function (href) {
						rbsBreadcrumbConfig.add('Location', element.index(), element.text(), href);
					});
				}
			}
		};
	});

	app.directive('rbsBreadcrumbConfig', ['RbsChange.Breadcrumb', function (Breadcrumb) {

		return {
			restrict : 'E',
			scope : true,
			require : '^rbsWorkspaceConfig',

			controller : ['$scope', function ($scope) {

				var bc = {
					'Location' : [],
					'Path' : []
				};

				this.add = function (type, index, text, href) {
					bc[type][index] = [text, href];
					if (bc[type].length === $scope.counts[type]) {
						Breadcrumb['set' + type](bc[type]);
					}
				};

			}],

			compile : function (tElement) {
				tElement.hide();
				return function linkFn (scope, element) {
					scope.counts = {
						'Location' : element.find('location').length,
						'Path' : element.find('path').length
					};
				};
			}
		};

	}]);

	app.directive('path', function () {
		return {
			restrict : 'E',
			require : '?^rbsBreadcrumbConfig',
			link : function (scope, element, attrs, rbsBreadcrumbConfig) {
				if (rbsBreadcrumbConfig) {
					attrs.$observe('href', function (href) {
						rbsBreadcrumbConfig.add('Path', element.index()-element.prevAll('location').length, element.text(), href);
					});
				}
			}
		};
	});

	app.directive('rbsWorkspaceConfig', ['RbsChange.Workspace', 'RbsChange.MainMenu', function (Workspace, MainMenu) {
		return {
			restrict : 'E',
			scope : true,
			controller : ['$scope', '$attrs', function ($scope, $attrs) {
				if ($attrs.menu) {
					MainMenu.loadModuleMenu($attrs.menu);
				}
			}]
		};

	}]);

})();<|endoftext|>


<commit_before>const FastBootAppServer = require('fastboot-app-server');

let server = new FastBootAppServer({
  distPath: 'dist',
  gzip: true,
  host: '0.0.0.0',
  port: 80,
  sandboxGlobals: { PASSENGER_APP_ENV: 'production' },
  chunkedResponse: true
});

server.start();<commit_msg>pass env variable to fastboot<commit_after>const FastBootAppServer = require('fastboot-app-server');

let server = new FastBootAppServer({
  distPath: 'dist',
  gzip: true,
  host: '0.0.0.0',
  port: 80,
  sandboxGlobals: { PASSENGER_APP_ENV: process.env.PASSENGER_APP_ENV },
  chunkedResponse: true
});

server.start();<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={actions.getPosts}>Load Posts</button>
      <button onClick={() => actions.getPost(1)}>Load Post</button>
      <button onClick={actions.getPost}>Load Post2</button>

Tags

      <button onClick={actions.getTags}>Load Tags</button>
      <button onClick={actions.getTag}>Load Tag</button>

Users

      <button onClick={actions.getUsers}>Load Users</button>
      <button onClick={actions.getUser}>Load User</button>

Reset

      <button onClick={actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);
<commit_msg>Add parameter to get single post/user/tag<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={actions.getPosts}>Load Posts</button>
      <button onClick={() => actions.getPost(1)}>Load Post</button>

Tags

      <button onClick={actions.getTags}>Load Tags</button>
      <button onClick={() => actions.getTag(1)}>Load Tag</button>

Users

      <button onClick={actions.getUsers}>Load Users</button>
      <button onClick={() => actions.getUser(1)}>Load User</button>

Reset

      <button onClick={actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);
<|endoftext|>


<commit_before>var util = require('util');
var Batch = require('batch');
var EventEmitter = require('events').EventEmitter;

var redis = require('./redis');
var debug = require('debug')('qp:Job');

var f = function(e,r){
  if (e) console.log(e);
};

var Job = module.exports = function(queue, data, opts) {
  this.data = data;
  this.queue = queue;
  this.state = 'unsaved';
  this.redis = redis.client();

  // doesnt currently do anything, the job uses the queue options
  this.opts = opts || {};
};

util.inherits(Job, EventEmitter);

Job.prototype.getID = function(cb) {
  var self = this;

  debug('getting id');

  // this allows the user to set an ID themselves
  if (this.id) return cb(null, this.id);

  // if no id is set, increment a counter and use that (redis round trip, not recommended)
  this.redis.incr('qp:' + this.queue.name + '.ids', function(err, id) {
    self.id = id;
    cb(err, id);
  });
};

Job.prototype.save = function(cb) {
  var self = this;

  var r = self.redis.multi();

  self._save(r, function(err) {
    if (err) return cb(err);

    r.exec(cb);
  });

  return this;
};

Job.prototype._save = function(r, cb) {
  var self = this;

  debug('saving');

  var save = function() {
    self._saved = true;

    self
      .set('type', self.queue.name, r)
      .set('created_at', Date.now(), r)
      .set('data', self.data, r);

    if (self._timeout) {
      self.set('_timeout', self._timeout, r);
    }

    if (self._attempts) {
      self.set('_attempts', self._attempts, r);
    }

    self.enqueue(r);

    cb();
  };

  self.getID(function(err, id) {

    if (self.queue.getOption('unique')) {
      debug('checking uniqueness');

      self.redis.sadd('qp:' + self.queue.name + ':unique', id, function(err, res) {
        // check if added
        if (!res) {
          debug('already queued');
          return cb(err, res);
        }

        save();
      });

      return;
    }

    save();

  });
};

Job.prototype.enqueue = function(r) {

  debug('enqueueing');

  if (this._attempts) r.hincrby('qp:job:' + this.queue.name + '.' + this.id, '_attempted', 1);
  r.rpush('qp:' + this.queue.name +':jobs', this.id);

  this.setState('inactive', r);

  return this;
};

Job.prototype.set = function(key, val, r, cb){
  this[key] = val;

  if (!this._saved || this.queue.getOption('noInfo')) {
    if (cb) cb();
    return this;
  }

  if (typeof val === 'object') val = JSON.stringify(val || {});

  (r || this.redis).hset('qp:job:' + this.queue.name + '.' + this.id, key, val, cb || f);

  return this;
};

Job.prototype.setState = function(state, r, cb) {
  if (!r) r = this.redis;

  debug('setting state');

  var zsets = this.queue.getOption('zsets');

  if (zsets) r.zrem('qp:' + this.queue.name + '.' + this.state, this.id, f);

  this.state = state;

  this.set('state', state, r);

  if (zsets) r.zadd('qp:' + this.queue.name + '.' + state, Date.now(), this.id, f);

  this._emit('state', state, r);

  return this;

};

Job.prototype.getInfo = function(cb) {
  var self = this;

  if (!this.id) return cb(null);

  debug('getting info');

  this.redis.hgetall('qp:job:' + this.queue.name + '.' + this.id, function(err, data) {
    if (err || !data){
      return cb(err);
    }

    self._processInfo(data);
    cb();
  });

};

Job.prototype._processInfo = function(info) {
  if(typeof info != 'object') return false;

  try {
    this.data = JSON.parse(info.data || {});
  } catch(e) {
    console.log('error parsing json');
    console.error(e);
    this.data = {};
  }
  this.state = info.state;
  this.type = info.type;
  this.created_at = new Date(+info.created_at);
  this._progress = info._progress;
  this._attempts = info._attempts && !isNaN(+info._attempts) && +info._attempts;
  this._attempted = +(info._attempted || 0);
  this._timeout = info._timeout && !isNaN(+info._timeout) && +info._timeout;
  this.error_message = info.error_message;

  if (info.completed_at) this.completed_at = new Date(+info.completed_at);
  if (info.updated_at) this.updated_at = new Date(+info.updated_at);

};

Job.prototype.toJSON = function(set) {
  var json = {
    id: this.id,
    queue: this.queue.name,
    data: this.data,
    state: this.state,
    created_at: this.created_at,
    progress: this._progress,
    completed_at: this.completed_at,
    attempts: this._attempts,
    attempted: this._attempted,
    timeout: this._timeout,
    error_message: this.error_message
  };
  if (set) this.json = json;
  return json;
};

Job.prototype.progress = function(done, total) {
  var progress = ~~(done / total * 100);

  debug('setting progress');

  this.set('_progress', progress);

  this._emit('progress', {
    done: done,
    total: total,
    progress: progress
  });

  return this;
};

Job.prototype.attempts = function(num) {
  this.set('_attempts', num);

  return this;
};

Job.prototype.timeout = function(num) {
  this.set('_timeout', num);

  return this;
};

Job.prototype.log = function(msg) {
  this.redis.rpush('qp:job:' + this.queue.name + ':log.' + this.id, msg);
  this._emit('log', msg);

  return this;
};

Job.prototype.getLog = function(cb) {
  this.redis.lrange('qp:job:' + this.queue.name + ':log.' + this.id, 0, -1, cb);
};

Job.prototype._emit = function(type, msg, r) {
  if (!r) r = this.redis;

  var data = {
    type: type,
    queue: this.queue.name,
    job: this.toJSON(),
    message: msg
  };

  this.emit(type, data);

  if (this.queue.getOption('pubSub') !== false) r.publish('qp:events', JSON.stringify(data));
};

Job.prototype.remove = function(cb) {
  var r = this.redis.multi();
  this._remove(r);

  if(r.queue.length > 2){
    r.exec(cb);
  }else if(r.queue.length > 1){
    var args = r.queue[1];
    var cmd = args.shift();
    this.redis[cmd](args, cb);
  }
};

Job.prototype._remove = function(r) {

  if (this.queue.getOption('zsets')) r.zrem('qp:' + this.queue.name + '.' + this.state, this.id);
  r.del('qp:job:' + this.queue.name + '.' + this.id,
        'qp:job:' + this.queue.name + ':log.' + this.id);

  if (this.queue.getOption('unique')) {
    r.srem('qp:' + this.queue.name + ':unique', this.id);
  }
};

Job.prototype._finish = function(r) {
  var self = this;

  self.finished = true;

  debug('finishing job');

  // this is handy for fast queues and for unique queues (avoids data conflicts if id's are reused)
  if (self.queue.getOption('deleteOnFinish')) {
    self._remove(r);
  } else if (self.queue.getOption('unique')) {
    r.srem('qp:' + self.queue.name + ':unique', self.id);
  }

  // we dont actually have any commands to do
  if (r.queue.length === 1) {
    self._emit('done');
    return;
  }

  r.exec(function() {
    self._emit('done');
  });
};

Job.prototype.error = function(err) {
  var self = this;

  self.log('attempt failed:' + err);

  if (self._attempts && (self._attempts > self._attempted || !self._attempted)) {
    var r = self.redis.multi();
    debug('requeueing');
    self.enqueue(r);
    self.finished = true;
    r.exec(function() {
      self._emit('done');
    });
    return;
  }

  this.fail(err);
};

Job.prototype.fail = function(err) {
  var self = this;

  debug('failed job');

  var r = this.redis.multi();

  this
    .set('failed_at', Date.now(), r)
    .setState('failed', r)
    .set('error_message', err);

  this._finish(r);

};

Job.prototype.done = function(err) {
  var self = this;

  if (self.finished) {
    debug('already finished');
    return;
  }

  debug('completed job');

  // clear any fail timeout if there is one
  clearTimeout(self.__timeout);
  if (err) {
    self.error(err);
    return;
  }

  var r = this.redis.multi();

  this
    .set('completed_at', Date.now(), r)
    .setState('completed', r);

  this._finish(r);
};

<commit_msg>style tweaks<commit_after>var util = require('util');
var Batch = require('batch');
var EventEmitter = require('events').EventEmitter;

var redis = require('./redis');
var debug = require('debug')('qp:Job');

var f = function(e,r){
  if (e) console.log(e);
};

var Job = module.exports = function(queue, data, opts) {
  this.data = data;
  this.queue = queue;
  this.state = 'unsaved';
  this.redis = redis.client();

  // doesnt currently do anything, the job uses the queue options
  this.opts = opts || {};
};

util.inherits(Job, EventEmitter);

Job.prototype.getID = function(cb) {
  var self = this;

  debug('getting id');

  // this allows the user to set an ID themselves
  if (this.id) return cb(null, this.id);

  // if no id is set, increment a counter and use that (redis round trip, not recommended)
  this.redis.incr('qp:' + this.queue.name + '.ids', function(err, id) {
    self.id = id;
    cb(err, id);
  });
};

Job.prototype.save = function(cb) {
  var self = this;

  var r = self.redis.multi();

  self._save(r, function(err) {
    if (err) return cb(err);

    r.exec(cb);
  });

  return this;
};

Job.prototype._save = function(r, cb) {
  var self = this;

  debug('saving');

  var save = function() {
    self._saved = true;

    self
      .set('type', self.queue.name, r)
      .set('created_at', Date.now(), r)
      .set('data', self.data, r);

    if (self._timeout) {
      self.set('_timeout', self._timeout, r);
    }

    if (self._attempts) {
      self.set('_attempts', self._attempts, r);
    }

    self.enqueue(r);

    cb();
  };

  self.getID(function(err, id) {

    if (self.queue.getOption('unique')) {
      debug('checking uniqueness');

      self.redis.sadd('qp:' + self.queue.name + ':unique', id, function(err, res) {
        // check if added
        if (!res) {
          debug('already queued');
          return cb(err, res);
        }

        save();
      });

      return;
    }

    save();

  });
};

Job.prototype.enqueue = function(r) {

  debug('enqueueing');

  if (this._attempts) r.hincrby('qp:job:' + this.queue.name + '.' + this.id, '_attempted', 1);
  r.rpush('qp:' + this.queue.name +':jobs', this.id);

  this.setState('inactive', r);

  return this;
};

Job.prototype.set = function(key, val, r, cb){
  this[key] = val;

  if (!this._saved || this.queue.getOption('noInfo')) {
    if (cb) cb();
    return this;
  }

  if (typeof val === 'object') val = JSON.stringify(val || {});

  (r || this.redis).hset('qp:job:' + this.queue.name + '.' + this.id, key, val, cb || f);

  return this;
};

Job.prototype.setState = function(state, r, cb) {
  if (!r) r = this.redis;

  debug('setting state');

  var zsets = this.queue.getOption('zsets');

  if (zsets) r.zrem('qp:' + this.queue.name + '.' + this.state, this.id, f);

  this.state = state;

  this.set('state', state, r);

  if (zsets) r.zadd('qp:' + this.queue.name + '.' + state, Date.now(), this.id, f);

  this._emit('state', state, r);

  return this;

};

Job.prototype.getInfo = function(cb) {
  var self = this;

  if (!this.id) return cb(null);

  debug('getting info');

  this.redis.hgetall('qp:job:' + this.queue.name + '.' + this.id, function(err, data) {
    if (err || !data){
      return cb(err);
    }

    self._processInfo(data);
    cb();
  });

};

Job.prototype._processInfo = function(info) {
  if(typeof info != 'object') return false;

  try {
    this.data = JSON.parse(info.data || {});
  } catch(e) {
    console.log('error parsing json');
    console.error(e);
    this.data = {};
  }
  this.state = info.state;
  this.type = info.type;
  this.created_at = new Date(+info.created_at);
  this._progress = info._progress;
  this._attempts = info._attempts && !isNaN(+info._attempts) && +info._attempts;
  this._attempted = +(info._attempted || 0);
  this._timeout = info._timeout && !isNaN(+info._timeout) && +info._timeout;
  this.error_message = info.error_message;

  if (info.completed_at) this.completed_at = new Date(+info.completed_at);
  if (info.updated_at) this.updated_at = new Date(+info.updated_at);

};

Job.prototype.toJSON = function(set) {
  var json = {
    id: this.id,
    queue: this.queue.name,
    data: this.data,
    state: this.state,
    created_at: this.created_at,
    progress: this._progress,
    completed_at: this.completed_at,
    attempts: this._attempts,
    attempted: this._attempted,
    timeout: this._timeout,
    error_message: this.error_message
  };
  if (set) this.json = json;
  return json;
};

Job.prototype.progress = function(done, total) {
  var progress = ~~(done / total * 100);

  debug('setting progress');

  this.set('_progress', progress);

  this._emit('progress', {
    done: done,
    total: total,
    progress: progress
  });

  return this;
};

Job.prototype.attempts = function(num) {
  this.set('_attempts', num);

  return this;
};

Job.prototype.timeout = function(num) {
  this.set('_timeout', num);

  return this;
};

Job.prototype.log = function(msg) {
  this.redis.rpush('qp:job:' + this.queue.name + ':log.' + this.id, msg);
  this._emit('log', msg);

  return this;
};

Job.prototype.getLog = function(cb) {
  this.redis.lrange('qp:job:' + this.queue.name + ':log.' + this.id, 0, -1, cb);
};

Job.prototype._emit = function(type, msg, r) {
  if (!r) r = this.redis;

  var data = {
    type: type,
    queue: this.queue.name,
    job: this.toJSON(),
    message: msg
  };

  this.emit(type, data);

  if (this.queue.getOption('pubSub') !== false) r.publish('qp:events', JSON.stringify(data));
};

Job.prototype.remove = function(cb) {
  var r = this.redis.multi();
  this._remove(r);

  if (r.queue.length > 2) {
    r.exec(cb);
  } else if (r.queue.length > 1) {

    // we have a single command in the multi, extract it out
    var args = r.queue[1];
    var cmd = args.shift();
    this.redis[cmd](args, cb);
  }
};

Job.prototype._remove = function(r) {

  if (this.queue.getOption('zsets')) r.zrem('qp:' + this.queue.name + '.' + this.state, this.id);

  r.del(
    'qp:job:' + this.queue.name + '.' + this.id,
    'qp:job:' + this.queue.name + ':log.' + this.id
  );

  if (this.queue.getOption('unique')) r.srem('qp:' + this.queue.name + ':unique', this.id);
};

Job.prototype._finish = function(r) {
  var self = this;

  self.finished = true;

  debug('finishing job');

  // this is handy for fast queues and for unique queues (avoids data conflicts if id's are reused)
  if (self.queue.getOption('deleteOnFinish')) {
    self._remove(r);
  } else if (self.queue.getOption('unique')) {
    r.srem('qp:' + self.queue.name + ':unique', self.id);
  }

  // we dont actually have any commands to do
  if (r.queue.length === 1) {
    self._emit('done');
    return;
  }

  r.exec(function() {
    self._emit('done');
  });
};

Job.prototype.error = function(err) {
  var self = this;

  self.log('attempt failed:' + err);

  if (self._attempts && (self._attempts > self._attempted || !self._attempted)) {
    var r = self.redis.multi();
    debug('requeueing');
    self.enqueue(r);
    self.finished = true;
    r.exec(function() {
      self._emit('done');
    });
    return;
  }

  this.fail(err);
};

Job.prototype.fail = function(err) {
  var self = this;

  debug('failed job');

  var r = this.redis.multi();

  this
    .set('failed_at', Date.now(), r)
    .setState('failed', r)
    .set('error_message', err);

  this._finish(r);

};

Job.prototype.done = function(err) {
  var self = this;

  if (self.finished) {
    debug('already finished');
    return;
  }

  debug('completed job');

  // clear any fail timeout if there is one
  clearTimeout(self.__timeout);
  if (err) {
    self.error(err);
    return;
  }

  var r = this.redis.multi();

  this
    .set('completed_at', Date.now(), r)
    .setState('completed', r);

  this._finish(r);
};

<|endoftext|>


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

/** run from commandline:
 * node index.js
 * -master /home/alexey/IdeaProjects/eslint-teamcity-failed-conditions/spec/fixtures/error.json
 * -current /home/alexey/IdeaProjects/eslint-teamcity-failed-conditions/spec/fixtures/empty.json
 */
/* eslint-env es6:true */
'use-strict';
const fs = require('fs-extra'),
      path = require('path'),
      isEqual = require('lodash.isequal'),
      clone = require('lodash.clone'),
      procArg = process.argv;
let currentJSON,
    masterJSON,
    resultJSONPath = path.resolve(path.dirname(procArg[1]), `result.json`);

if (require.main === module) {
  currentJSON = fs.readJSON(`${procArg[procArg.indexOf('-current') + 1]}`);
  masterJSON = fs.readJSON(`${procArg[procArg.indexOf('-master') + 1]}`);
  resultJSONPath = path.resolve(procArg.indexOf('-result') !== -1 ? procArg[procArg.indexOf('-result') + 1] : resultJSONPath);
  main(masterJSON, currentJSON, resultJSONPath);
} else {
  module.exports = main;
}

// FIXME получать в виде объекта, использовать деструктуризацию
/**
 * Входная точка
 * @param {Object} masterJSON - json с мастер ветки
 * @param {Object} currentJSON - текущий json
 * @param {String} [_resultJSONPath] - путь, куда записывать результат
 * @return {Promise.<Array>} - результат мержа
 */
function main (masterJSON, currentJSON, _resultJSONPath) {
  return Promise.all([masterJSON, currentJSON]).then((resultObjectArrays) => {
    return {
      master: customEslintResultsIterator(resultObjectArrays[0]),
      current: customEslintResultsIterator(resultObjectArrays[1])
    };
  }).then((resultMapsObject) => {
    return getUnicalNewErrors(resultMapsObject.master, resultMapsObject.current);
  }).then((unicalErrors) => {
    // TODO: вынести запись в файл из модуля
    writeResult(unicalErrors, _resultJSONPath || resultJSONPath);
    return unicalErrors;
  });
};

/**
 * Получение результатов проверки с уникальными сообщений об ошибках
 * @param {LintResult} lintResultCurrent - текущая
 * @param {LintResult} lintResultMaster - мастер
 * @return {LintResult} рузультат проверки с уникальными ошибками
 */
function getLintResultWithUnicalNewMessages (lintResultCurrent, lintResultMaster) {
  let unicalNewMessages = clone(lintResultCurrent),
      result,
      warningCount = 0,
      errorCount = 0;

  unicalNewMessages.messages = lintResultCurrent.messages.filter((item, i) => {
    let elementExist;

    elementExist = lintResultMaster.messages.some((masterItem) => {
      return isEqual(item, masterItem);
    });

    return !elementExist;
  }, 0).map((item) => {
    item.severity.toString() === '1' ? warningCount++ : errorCount++;
    return item;
  });

  unicalNewMessages.warningCount = warningCount;
  unicalNewMessages.errorCount = errorCount;

  if (unicalNewMessages.messages.length > 0) {
    result = unicalNewMessages;
  }
  return result;
}

/**
 * Получить уникальные, новые ошибки. Старые берутся с master ветки
 * @param {Map.<EslintErrorFileName, LintResult[]>} errorsMapMaster ошибки ветки
 * @param {Map.<EslintErrorFileName, LintResult[]>} errorsMapCurrent ошибки текущей ветки
 * @return {LintResult[]} обработанные результаты проверок. Только уникальные
 */
function getUnicalNewErrors (errorsMapMaster, errorsMapCurrent) {
  let errorsMapMasterEmpty = errorsMapMaster.size === 0,
      errorsMapCurrentEmpty = errorsMapCurrent.size === 0,
      unicalNewErrors = new Map(),
      lintResultWithUnicalNewMessages;

  if (!errorsMapCurrentEmpty) {
    if (errorsMapMasterEmpty) {
      unicalNewErrors = [...errorsMapMasterEmpty.values()];
    } else {
      errorsMapCurrent.forEach((value, key) => {
        let masterHasErrorInFile = errorsMapMaster.has(key),
            errorsInFileIsIden = isEqual(value, errorsMapMaster.get(key));

        if (masterHasErrorInFile && !errorsInFileIsIden) {
          lintResultWithUnicalNewMessages = getLintResultWithUnicalNewMessages(value, errorsMapMaster.get(key));
          if (lintResultWithUnicalNewMessages) {
            unicalNewErrors.set(key, lintResultWithUnicalNewMessages);
          }
        } else if (!errorsInFileIsIden && value.messages.length > 0) {
          unicalNewErrors.set(key, value);
        }
      });
    }
  }
  return [...unicalNewErrors.values()];
}

/**
 *
 * @param {LintResult} unicalErrors
 * @param {String} _resultJSONPath
 */
function writeResult (unicalErrors, _resultJSONPath) {
  fs.writeJSONSync(_resultJSONPath, unicalErrors);
}

/**
 * Результаты проверок файлов линтером Eslint
 * @typedef {LintResult[]} EslintResults
 */

/**
 * Имя файла, в котором найдены ошибки
 * @typedef {String} EslintErrorFileName
 */

/**
 * A linting warning or error.
 * @typedef {Object} LintMessage
 * @property {string} message The message to display to the user.
 */

/**
 * A linting result.
 * @typedef {Object} LintResult
 * @property {string} filePath The path to the file that was linted.
 * @property {LintMessage[]} messages All of the messages for the result.
 * @property {number} errorCount Number of errors for the result.
 * @property {number} warningCount Number of warnings for the result.
 * @property {number} fixableErrorCount Number of fixable errors for the result.
 * @property {number} fixableWarningCount Number of fixable warnings for the result.
 * @property {string=} [source] The source code of the file that was linted.
 * @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
 */

/**
 * Итератор по результату eslint
 * @param {EslintResults} _resultObjectArray
 * @return {Map<filePath, LintResult>} объект с key - имя файла с
 * ошибкой
 */
function customEslintResultsIterator (_resultObjectArray) {
  let resultMap = new Map();

  if (_resultObjectArray.length === 1 &&
        Object.keys(_resultObjectArray[0]).length === 0) {
    return resultMap;
  }

  _resultObjectArray.forEach((item) => {
    resultMap.set(item.filePath, item);
  });

  return resultMap;
}
<commit_msg>chore(*): частично переработана функция getUnicalNewErrors<commit_after>#!/usr/bin/env node

/** run from commandline:
 * node index.js
 * -master /home/alexey/IdeaProjects/eslint-teamcity-failed-conditions/spec/fixtures/error.json
 * -current /home/alexey/IdeaProjects/eslint-teamcity-failed-conditions/spec/fixtures/empty.json
 */
/* eslint-env es6:true */
'use-strict';
const fs = require('fs-extra'),
      path = require('path'),
      isEqual = require('lodash.isequal'),
      clone = require('lodash.clone'),
      procArg = process.argv;
let currentJSON,
    masterJSON,
    resultJSONPath = path.resolve(path.dirname(procArg[1]), `result.json`);

if (require.main === module) {
  currentJSON = fs.readJSON(`${procArg[procArg.indexOf('-current') + 1]}`);
  masterJSON = fs.readJSON(`${procArg[procArg.indexOf('-master') + 1]}`);
  resultJSONPath = path.resolve(procArg.indexOf('-result') !== -1 ? procArg[procArg.indexOf('-result') + 1] : resultJSONPath);
  main(masterJSON, currentJSON, resultJSONPath);
} else {
  module.exports = main;
}

// FIXME получать в виде объекта, использовать деструктуризацию
/**
 * Входная точка
 * @param {Object} masterJSON - json с мастер ветки
 * @param {Object} currentJSON - текущий json
 * @param {String} [_resultJSONPath] - путь, куда записывать результат
 * @return {Promise.<Array>} - результат мержа
 */
function main (masterJSON, currentJSON, _resultJSONPath) {
  return Promise.all([masterJSON, currentJSON]).then((resultObjectArrays) => {
    return {
      master: customEslintResultsIterator(resultObjectArrays[0]),
      current: customEslintResultsIterator(resultObjectArrays[1])
    };
  }).then((resultMapsObject) => {
    return getUnicalNewErrors(resultMapsObject.master, resultMapsObject.current);
  }).then((unicalErrors) => {
    // TODO: вынести запись в файл из модуля
    writeResult(unicalErrors, _resultJSONPath || resultJSONPath);
    return unicalErrors;
  });
};

/**
 * Получение результатов проверки с уникальными сообщений об ошибках
 * @param {LintResult} lintResultCurrent - текущая
 * @param {LintResult} lintResultMaster - мастер
 * @return {LintResult} рузультат проверки с уникальными ошибками
 */
function getLintResultWithUnicalNewMessages (lintResultCurrent, lintResultMaster) {
  let unicalNewMessages = clone(lintResultCurrent),
      result,
      warningCount = 0,
      errorCount = 0;

  unicalNewMessages.messages = lintResultCurrent.messages.filter((item) => {
    let elementExist;

    elementExist = lintResultMaster.messages.some((masterItem) => {
      return isEqual(item, masterItem);
    });

    return !elementExist;
  }, 0).map((item) => {
    item.severity.toString() === '1' ? warningCount++ : errorCount++;
    return item;
  });

  unicalNewMessages.warningCount = warningCount;
  unicalNewMessages.errorCount = errorCount;

  if (unicalNewMessages.messages.length > 0) {
    result = unicalNewMessages;
  }
  return result;
}

/**
 * Получить уникальные, новые ошибки. Старые берутся с master ветки
 * @param {Map.<EslintErrorFileName, LintResult[]>} errorsMapCurrent ошибки текущей ветки
 * @param {Map.<EslintErrorFileName, LintResult[]>} errorsMapMaster ошибки ветки
 * @return {LintResult[]} обработанные результаты проверок. Только уникальные
 */
function getUnicalNewErrors (errorsMapMaster, errorsMapCurrent) {
  let errorsMapMasterNotEmpty = errorsMapMaster.size > 0,
      errorsMapCurrentNotEmpty = errorsMapCurrent.size > 0,
      unicalNewErrors = new Map(),
      lintResultWithUnicalNewMessages;

  if (errorsMapCurrentNotEmpty) {
    // Если есть описания ошибок для текущей проверки
    errorsMapCurrent.forEach((value, key) => {
      let masterHasDescriptionOfFile = errorsMapMaster.has(key),
          masterDescriptionOfFile = errorsMapMaster.get(key);

      if (errorsMapMasterNotEmpty && masterHasDescriptionOfFile) {
        lintResultWithUnicalNewMessages = getLintResultWithUnicalNewMessages(value, masterDescriptionOfFile);
        if (lintResultWithUnicalNewMessages) {
          unicalNewErrors.set(key, lintResultWithUnicalNewMessages);
        }
      } else if (value.messages.length > 0) {
        // если в master json нет описания ошибок файла, и описание ошибок файла в текущем json не пусто
        unicalNewErrors.set(key, value);
      }
    });
  }
  return [...unicalNewErrors.values()];
}

/**
 *
 * @param {LintResult} unicalErrors
 * @param {String} _resultJSONPath
 */
function writeResult (unicalErrors, _resultJSONPath) {
  fs.writeJSONSync(_resultJSONPath, unicalErrors);
}

/**
 * Результаты проверок файлов линтером Eslint
 * @typedef {LintResult[]} EslintResults
 */

/**
 * Имя файла, в котором найдены ошибки
 * @typedef {String} EslintErrorFileName
 */

/**
 * A linting warning or error.
 * @typedef {Object} LintMessage
 * @property {string} message The message to display to the user.
 */

/**
 * A linting result.
 * @typedef {Object} LintResult
 * @property {string} filePath The path to the file that was linted.
 * @property {LintMessage[]} messages All of the messages for the result.
 * @property {number} errorCount Number of errors for the result.
 * @property {number} warningCount Number of warnings for the result.
 * @property {number} fixableErrorCount Number of fixable errors for the result.
 * @property {number} fixableWarningCount Number of fixable warnings for the result.
 * @property {string=} [source] The source code of the file that was linted.
 * @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
 */

/**
 * Итератор по результату eslint
 * @param {EslintResults} _resultObjectArray
 * @return {Map<filePath, LintResult>} объект с key - имя файла с
 * ошибкой
 */
function customEslintResultsIterator (_resultObjectArray) {
  let resultMap = new Map();

  if (_resultObjectArray.length === 1 &&
        Object.keys(_resultObjectArray[0]).length === 0) {
    return resultMap;
  }

  _resultObjectArray.forEach((item) => {
    resultMap.set(item.filePath, item);
  });

  return resultMap;
}
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={actions.getPosts}>Load Posts</button>
      <button onClick={() => actions.getPost(1)}>Load Post</button>

Tags

      <button onClick={actions.getTags}>Load Tags</button>
      <button onClick={() => actions.getTag(1)}>Load Tag</button>

Users

      <button onClick={actions.getUsers}>Load Users</button>
      <button onClick={() => actions.getUser(1)}>Load User</button>

Reset

      <button onClick={actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);
<commit_msg>Add and fix action buttons in example<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={() => actions.getPosts()}>Load Posts</button>
      <button onClick={() => actions.getPosts({ fields: 'title, slug' })}>Load Posts (title & slug only)</button>
      <button onClick={() => actions.getPost(1, { include: 'author' })}>Load Single Post</button>
      <button onClick={() => actions.getPost(1, { include: 'author' })}>Load Single Post (with author data)</button>

Tags

      <button onClick={() => actions.getTags()}>Load Tags</button>
      <button onClick={() => actions.getTag(1)}>Load Single Tag</button>

Users

      <button onClick={() => actions.getUsers()}>Load Users</button>
      <button onClick={() => actions.getUser(1)}>Load Single User</button>

Reset

      <button onClick={() => actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);
<|endoftext|>


<commit_before>"use strict";

/**
 * @type {Chai.Assert}
 */
var assert = require('chai').assert
var xdTesting = require('../../lib/index')
var templates = xdTesting.templates
/**
 * @type {Q}
 */
var q = require('q')

describe('XD-MVC Gallery @large', function () {
    var test = this

    // Set test timeout
    test.timeout(180 * 1000)
    test.baseUrl = "http://localhost:8082/gallery.html"
    test.adapter = require('../../lib/adapter/xdmvc')

    describe('eventLogger', () => {
        it('should count XDconnection events', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let loadedUrlA
            return xdTesting.multiremote(options).init()
                .url(test.baseUrl)
                .execute(test.adapter.injectEventLogger)
                .execute(test.adapter.getEventCounter)
                .then(function (retA) {
                    console.log(retA.value)
                    assert.equal(retA.value.XDconnection, 0)
                })
                // Share url from A to B
                .then(() => console.log('share url from A to B'))
                .getUrl().then(urlA => loadedUrlA = urlA)
                .selectById('B', B => B
                    .url(loadedUrlA)
                    .execute(test.adapter.injectEventLogger)
                )
                .then(() => console.log('shared url'))
                // Assert connection event
                .selectById('A', A => A
                    .waitUntil(() => A
                        .execute(test.adapter.getEventCounter)
                        .then(function (retA) {
                            return retA.value.XDconnection === 1
                        })
                    )
                )
                .execute(test.adapter.getEventCounter)
                .then(function (retA) {
                    console.log(retA.value)
                    assert.equal(retA.value.XDconnection, 1)
                })
                .end()
        })
    })

    it('should pair two devices via url', () => {
        let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}
        let deviceCount

        let devices = xdTesting.multiremote(options).init()
        devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
        return devices
            .then(() => console.log('paired'))
            .getCount().then(count => deviceCount = count)
            .selectById('A', device => device
                .then(() => console.log('get event counter..'))
                .execute(test.adapter.getEventCounter)
                .then(ret => {
                    console.log('got event counter', ret.value)
                    assert.equal(ret.value.XDconnection, deviceCount - 1)
                })
            )
            .end()
    });

    describe('prerequisites', () => {
        it('browsers should not share cookies between sessions', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let devices = xdTesting.multiremote(options).init()
            devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
            return devices
                .selectById('A', deviceA => deviceA
                    // Set cookie and verify it's there
                    .setCookie({name: 'test_cookieA', value: 'A'})
                    .getCookie('test_cookieA')
                    .then(cookie => {
                        assert.notEqual(cookie, null)
                        assert.equal(cookie.name, 'test_cookieA')
                        assert.equal(cookie.value, 'A')
                    })
                )
                .selectById('B', deviceB => deviceB
                    // Set another cookie and verify it's there
                    .setCookie({name: 'test_cookieB', value: 'B'})
                    .getCookie('test_cookieB').then(cookie => {
                        assert.notEqual(cookie, null)
                        assert.equal(cookie.name, 'test_cookieB')
                        assert.equal(cookie.value, 'B')
                    })
                    // Verify the cookie from the other browser is not here
                    .getCookie('test_cookieA')
                    .then(cookie => {
                        assert.equal(cookie, null)
                    })
                )
                .end()
        })

        it('browsers should not share local storage between sessions', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let devices = xdTesting.multiremote(options).init()
            devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
            return devices
                .selectById('A', deviceA => deviceA
                    // Set local storage item and verify it's there
                    .execute(() => localStorage.setItem('test_storageA', 'A'))
                    .execute(() => localStorage.getItem('test_storageA')).then(ret => assert.equal(ret.value, 'A'))
                )
                .selectById('B', deviceB => deviceB
                    // Verify the item from the other browser is not here
                    .execute(() => localStorage.getItem('test_storageA')).then(ret =>assert.equal(ret.value, null))
                )
                .end()
        })
    })

    describe('should show the selected image on the other devices', () => {

        xdTesting.loadSetups().forEach(setup => {

            // Assemble setup name
            let options = setup.devices
            let setupName = Object.keys(options).map(key => options[key].name).join(', ')

            it('on ' + setupName, function () {
                let imageUrlA
                let urlA
                let allButA

                let devices = xdTesting.multiremote(options).init()
                devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
                return devices
                    .name(setupName)
                    .checkpoint('load app')
                    .selectById('A', deviceA => deviceA
                        .waitForVisible('h2.gallery-overview')
                        .click('//*[text()="Bike Tours"]')
                        .waitForVisible('#gallery img:nth-of-type(1)')
                        .checkpoint('select album')
                        .click('#gallery img:nth-of-type(1)')
                        .waitForVisible('#image img')
                        .scroll('#image img')
                        .checkpoint('click thumbnail')
                        .getAttribute('#image img', 'src').then(src => {
                            imageUrlA = src;
                        })
                        .getUrl().then(function (url) {
                            urlA = url
                        })
                    )
                    .getDeviceIds().then(ret => {
                        let deviceIds = ret.value
                        allButA = deviceIds.filter(deviceId => deviceId != 'A')
                    })
                    .selectById(() => allButA, otherDevices => otherDevices
                        .waitForVisible('#image img')
                        .checkpoint('click thumbnail')
                        .getAttribute('#image img', 'src')
                    )
                    .then(function () {
                        let srcs = arguments
                        Object.keys(srcs).forEach(key => {
                            var src = srcs[key]
                            assert.equal(src, imageUrlA)
                        })
                    })
                    .checkpoint('end')
                    .end()
            })
        })
    })
})
<commit_msg>Reformat code, use arrow functions<commit_after>"use strict";

/**
 * @type {Chai.Assert}
 */
var assert = require('chai').assert
var xdTesting = require('../../lib/index')
var templates = xdTesting.templates
/**
 * @type {Q}
 */
var q = require('q')

describe('XD-MVC Gallery @large', function () {
    var test = this

    // Set test timeout
    test.timeout(180 * 1000)
    test.baseUrl = "http://localhost:8082/gallery.html"
    test.adapter = require('../../lib/adapter/xdmvc')

    describe('eventLogger', () => {
        it('should count XDconnection events', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let loadedUrlA
            return xdTesting.multiremote(options).init()
                .url(test.baseUrl)
                .execute(test.adapter.injectEventLogger)
                .execute(test.adapter.getEventCounter)
                .then(function (retA) {
                    console.log(retA.value)
                    assert.equal(retA.value.XDconnection, 0)
                })
                // Share url from A to B
                .then(() => console.log('share url from A to B'))
                .getUrl().then(urlA => loadedUrlA = urlA)
                .selectById('B', B => B
                    .url(loadedUrlA)
                    .execute(test.adapter.injectEventLogger)
                )
                .then(() => console.log('shared url'))
                // Assert connection event
                .selectById('A', A => A
                    .waitUntil(() => A
                        .execute(test.adapter.getEventCounter)
                        .then(function (retA) {
                            return retA.value.XDconnection === 1
                        })
                    )
                )
                .execute(test.adapter.getEventCounter)
                .then(function (retA) {
                    console.log(retA.value)
                    assert.equal(retA.value.XDconnection, 1)
                })
                .end()
        })
    })

    it('should pair two devices via url', () => {
        let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}
        let deviceCount

        let devices = xdTesting.multiremote(options).init()
        devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
        return devices
            .then(() => console.log('paired'))
            .getCount().then(count => deviceCount = count)
            .selectById('A', device => device
                .then(() => console.log('get event counter..'))
                .execute(test.adapter.getEventCounter)
                .then(ret => {
                    console.log('got event counter', ret.value)
                    assert.equal(ret.value.XDconnection, deviceCount - 1)
                })
            )
            .end()
    });

    describe('prerequisites', () => {
        it('browsers should not share cookies between sessions', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let devices = xdTesting.multiremote(options).init()
            devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
            return devices
                .selectById('A', deviceA => deviceA
                    // Set cookie and verify it's there
                    .setCookie({name: 'test_cookieA', value: 'A'})
                    .getCookie('test_cookieA')
                    .then(cookie => {
                        assert.notEqual(cookie, null)
                        assert.equal(cookie.name, 'test_cookieA')
                        assert.equal(cookie.value, 'A')
                    })
                )
                .selectById('B', deviceB => deviceB
                    // Set another cookie and verify it's there
                    .setCookie({name: 'test_cookieB', value: 'B'})
                    .getCookie('test_cookieB').then(cookie => {
                        assert.notEqual(cookie, null)
                        assert.equal(cookie.name, 'test_cookieB')
                        assert.equal(cookie.value, 'B')
                    })
                    // Verify the cookie from the other browser is not here
                    .getCookie('test_cookieA')
                    .then(cookie => {
                        assert.equal(cookie, null)
                    })
                )
                .end()
        })

        it('browsers should not share local storage between sessions', () => {
            let options = {A: templates.devices.chrome(), B: templates.devices.chrome()}

            let devices = xdTesting.multiremote(options).init()
            devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
            return devices
                .selectById('A', deviceA => deviceA
                    // Set local storage item and verify it's there
                    .execute(() => localStorage.setItem('test_storageA', 'A'))
                    .execute(() => localStorage.getItem('test_storageA')).then(ret => assert.equal(ret.value, 'A'))
                )
                .selectById('B', deviceB => deviceB
                    // Verify the item from the other browser is not here
                    .execute(() => localStorage.getItem('test_storageA')).then(ret =>assert.equal(ret.value, null))
                )
                .end()
        })
    })

    describe('should show the selected image on the other devices', () => {

        xdTesting.loadSetups().forEach(setup => {

            // Assemble setup name
            let options = setup.devices
            let setupName = Object.keys(options).map(key => options[key].name).join(', ')

            it('on ' + setupName, function () {
                let imageUrlA
                let urlA
                let allButA

                let devices = xdTesting.multiremote(options).init()
                devices = test.adapter.pairDevicesViaURL(devices, test.baseUrl)
                return devices
                    .name(setupName)
                    .checkpoint('load app')
                    .selectById('A', deviceA => deviceA
                        .waitForVisible('h2.gallery-overview')
                        .click('//*[text()="Bike Tours"]')
                        .waitForVisible('#gallery img:nth-of-type(1)')
                        .checkpoint('select album')
                        .click('#gallery img:nth-of-type(1)')
                        .waitForVisible('#image img')
                        .scroll('#image img')
                        .checkpoint('click thumbnail')
                        .getAttribute('#image img', 'src').then(src => imageUrlA = src)
                        .getUrl().then(url => urlA = url)
                    )
                    .getDeviceIds().then(ret => {
                        let deviceIds = ret.value
                        allButA = deviceIds.filter(deviceId => deviceId != 'A')
                    })
                    .selectById(() => allButA, otherDevices => otherDevices
                        .waitForVisible('#image img')
                        .checkpoint('click thumbnail')
                        .getAttribute('#image img', 'src')
                    )
                    .then(function () {
                        let srcs = arguments
                        Object.keys(srcs).forEach(key => {
                            var src = srcs[key]
                            assert.equal(src, imageUrlA)
                        })
                    })
                    .checkpoint('end')
                    .end()
            })
        })
    })
})
<|endoftext|>