<commit_before>/*jslint node: true */
'use strict';

var http = require('http');
var express = require('express');
var mongo = require('mongodb');
var settings = require('./settings.js');

var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;

module.exports = {};

function setup(app, db, idgen, collectionName) {

  // Database ..................................................................

  function getCollection(cb) {
    return db.collection(collectionName, cb);
  }

  function getOrCreate(user, callback) {

    getCollection(function(err, collection) {

      // If we're dealing with an existing document, we want to make sure we
      // find it. Necessary because we delete user._id later on.
      var query;
      var id;

      if (user.hasOwnProperty("_id")) {
        // First, cast to a string to make sure we've got a consistent datatype
        id = String(user._id);
        // Then build an objectId from the string
        query = {_id: new mongo.BSONPure.ObjectID(id)};
      }else {
        if (user.hasOwnProperty("email")) {
          query = {email: user.email};
        }else {
          query = user;
        }
      }

      console.log("Query: ", query);

      // Remove the id; it breaks findAndModify with upsert
      // ("Mod on _id not allowed")
      delete user._id;

      collection.findAndModify(
        query,                    // find
        [['_id','asc']],          // sort by
        {$set: user},             // update
        {upsert: true, new:true}, // create if new, return new obj
        function(err, object) {
          if (err) {
            console.warn(err.message);
          } else {
            console.log("Object found: ", object);  // undefined if no matching object exists.
          }
          callback(object);
        }
      );

    });
  }

  module.exports.getOrCreate = getOrCreate;

	// Use the FacebookStrategy within Passport.
	//   Strategies in Passport require a `verify` function, which accept
	//   credentials (in this case, an accessToken, refreshToken, and Facebook
	//   profile), and invoke a callback with a user object.
	passport.use(new FacebookStrategy({
	    clientID: settings.FACEBOOK_APP_ID,
	    clientSecret: settings.FACEBOOK_APP_SECRET,
	    callbackURL: "http://localhost:3000/auth/facebook/callback"
	  },
	  function(accessToken, refreshToken, profile, done) {
	    process.nextTick(function () {

	      // To keep the example simple, the user's Facebook profile is returned to
	      // represent the logged-in user.  In a typical application, you would want
	      // to associate the Facebook account with a user record in your database,
	      // and return that user instead.
	      return done(null, profile);
	    });
	  }
	));

  // Passport session setup.
  //   To support persistent login sessions, Passport needs to be able to
  //   serialize users into and deserialize users out of the session.  Typically,
  //   this will be as simple as storing the user ID when serializing, and finding
  //   the user by ID when deserializing.  However, since this example does not
  //   have a database of user records, the complete Facebook profile is serialized
  //   and deserialized.
  passport.serializeUser(function(userFromFacebook, done) {
    // console.log("Serializing:", userFromFacebook._json);

    getOrCreate(userFromFacebook._json, function(userFromDatabase){
      console.log(userFromDatabase);
      done(null, userFromDatabase);
    });
  });

  passport.deserializeUser(function(obj, done) {
    console.log("Deserializing:", obj);

    getOrCreate(obj, function(user) {
      console.log(user);
      user._id = String(user._id);
      done(null, user);
    });
  });

  // Some helpers we need to use
  app.use(express.cookieParser());
  app.use(express.session({ secret: settings.secret }));

  // Initialize Passport. Also use passport.session() middleware, to support
  // persistent login sessions (recommended).
  app.use(passport.initialize());
  app.use(passport.session());

  // Login routes ..............................................................

  // GET /auth/facebook
  //   Use passport.authenticate() as route middleware to authenticate the
  //   request.  The first step in Facebook authentication will involve
  //   redirecting the user to facebook.com.  After authorization, Facebook will
  //   redirect the user back to this application at /auth/facebook/callback
  app.get('/auth/facebook',
    passport.authenticate('facebook', { scope: [ 'email' ] }),
    function(req, res) {
      // The request will be redirected to Facebook for authentication, so this
      // function will not be called.
    }
  );

  // GET /auth/facebook/callback
  //   Use passport.authenticate() as route middleware to authenticate the
  //   request.  If authentication fails, the user will be redirected back to the
  //   login page.  Otherwise, the primary route function function will be called,
  //   which, in this example, will redirect the user to the home page.
  app.get('/auth/facebook/callback',
    passport.authenticate('facebook', { failureRedirect: '/login' }),
    function(req, res) {
      res.redirect('/index.html');
    }
  );

  // GET /logout
  //  Does what you think it does.
  app.get('/logout', function(req, res){
    req.logout();
    res.redirect('/');
  });

  // GET /user
  //  Return details about the current user
  //  Return a 401 if there isn't a current user
  app.get('/api/user', function(req, response){
    console.log(req.isAuthenticated());
    if(req.isAuthenticated()) {
      response.send(req.user);
    }else {
      response.send(401);
    }
  });

}

// Utility / middleware ........................................................

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.
//   Otherwise, the user will be sent a 401.
function ensureAuthenticated(req, res, next) {
  console.log("Checking if authenticated");
  if (req.isAuthenticated()) { return next(); }
  res.send(401);
}

module.exports = {
  setup: setup,
  ensureAuthenticated: ensureAuthenticated,
};
<commit_msg>Use a relative URL as the auth redirect<commit_after>/*jslint node: true */
'use strict';

var http = require('http');
var express = require('express');
var mongo = require('mongodb');
var settings = require('./settings.js');

var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;

module.exports = {};

function setup(app, db, idgen, collectionName) {

  // Database ..................................................................

  function getCollection(cb) {
    return db.collection(collectionName, cb);
  }

  function getOrCreate(user, callback) {

    getCollection(function(err, collection) {

      // If we're dealing with an existing document, we want to make sure we
      // find it. Necessary because we delete user._id later on.
      var query;
      var id;

      if (user.hasOwnProperty("_id")) {
        // First, cast to a string to make sure we've got a consistent datatype
        id = String(user._id);
        // Then build an objectId from the string
        query = {_id: new mongo.BSONPure.ObjectID(id)};
      }else {
        if (user.hasOwnProperty("email")) {
          query = {email: user.email};
        }else {
          query = user;
        }
      }

      console.log("Query: ", query);

      // Remove the id; it breaks findAndModify with upsert
      // ("Mod on _id not allowed")
      delete user._id;

      collection.findAndModify(
        query,                    // find
        [['_id','asc']],          // sort by
        {$set: user},             // update
        {upsert: true, new:true}, // create if new, return new obj
        function(err, object) {
          if (err) {
            console.warn(err.message);
          } else {
            console.log("Object found: ", object);  // undefined if no matching object exists.
          }
          callback(object);
        }
      );

    });
  }

  module.exports.getOrCreate = getOrCreate;

	// Use the FacebookStrategy within Passport.
	//   Strategies in Passport require a `verify` function, which accept
	//   credentials (in this case, an accessToken, refreshToken, and Facebook
	//   profile), and invoke a callback with a user object.
	passport.use(new FacebookStrategy({
	    clientID: settings.FACEBOOK_APP_ID,
	    clientSecret: settings.FACEBOOK_APP_SECRET,
	    callbackURL: "/auth/facebook/callback" //http://localhost:3000
	  },
	  function(accessToken, refreshToken, profile, done) {
	    process.nextTick(function () {

	      // To keep the example simple, the user's Facebook profile is returned to
	      // represent the logged-in user.  In a typical application, you would want
	      // to associate the Facebook account with a user record in your database,
	      // and return that user instead.
	      return done(null, profile);
	    });
	  }
	));

  // Passport session setup.
  //   To support persistent login sessions, Passport needs to be able to
  //   serialize users into and deserialize users out of the session.  Typically,
  //   this will be as simple as storing the user ID when serializing, and finding
  //   the user by ID when deserializing.  However, since this example does not
  //   have a database of user records, the complete Facebook profile is serialized
  //   and deserialized.
  passport.serializeUser(function(userFromFacebook, done) {
    // console.log("Serializing:", userFromFacebook._json);

    getOrCreate(userFromFacebook._json, function(userFromDatabase){
      console.log(userFromDatabase);
      done(null, userFromDatabase);
    });
  });

  passport.deserializeUser(function(obj, done) {
    console.log("Deserializing:", obj);

    getOrCreate(obj, function(user) {
      console.log(user);
      user._id = String(user._id);
      done(null, user);
    });
  });

  // Some helpers we need to use
  app.use(express.cookieParser());
  app.use(express.session({ secret: settings.secret }));

  // Initialize Passport. Also use passport.session() middleware, to support
  // persistent login sessions (recommended).
  app.use(passport.initialize());
  app.use(passport.session());

  // Login routes ..............................................................

  // GET /auth/facebook
  //   Use passport.authenticate() as route middleware to authenticate the
  //   request.  The first step in Facebook authentication will involve
  //   redirecting the user to facebook.com.  After authorization, Facebook will
  //   redirect the user back to this application at /auth/facebook/callback
  app.get('/auth/facebook',
    passport.authenticate('facebook', { scope: [ 'email' ] }),
    function(req, res) {
      // The request will be redirected to Facebook for authentication, so this
      // function will not be called.
    }
  );

  // GET /auth/facebook/callback
  //   Use passport.authenticate() as route middleware to authenticate the
  //   request.  If authentication fails, the user will be redirected back to the
  //   login page.  Otherwise, the primary route function function will be called,
  //   which, in this example, will redirect the user to the home page.
  app.get('/auth/facebook/callback',
    passport.authenticate('facebook', { failureRedirect: '/login' }),
    function(req, res) {
      res.redirect('/index.html');
    }
  );

  // GET /logout
  //  Does what you think it does.
  app.get('/logout', function(req, res){
    req.logout();
    res.redirect('/');
  });

  // GET /user
  //  Return details about the current user
  //  Return a 401 if there isn't a current user
  app.get('/api/user', function(req, response){
    console.log(req.isAuthenticated());
    if(req.isAuthenticated()) {
      response.send(req.user);
    }else {
      response.send(401);
    }
  });

}

// Utility / middleware ........................................................

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.
//   Otherwise, the user will be sent a 401.
function ensureAuthenticated(req, res, next) {
  console.log("Checking if authenticated");
  if (req.isAuthenticated()) { return next(); }
  res.send(401);
}

module.exports = {
  setup: setup,
  ensureAuthenticated: ensureAuthenticated,
};
<|endoftext|>


<commit_before>'use strict';

const	topLogPrefix = 'larvituser: users.js ',
	LUtils	= require('larvitutils'),
	async	= require('async');

function Users(options) {
	const	logPrefix	= topLogPrefix + 'Users() - ',
		that	= this;

	that.options	= options || {};

	if ( ! that.options.log) {
		const	tmpLUtils	= new LUtils();
		options.log	= new tmpLUtils.Log();
	}

	that.options	= options;

	for (const key of Object.keys(options)) {
		that[key]	= options[key];
	}

	that.lUtils	= new LUtils({'log': that.log});

	if ( ! that.db) {
		const	err	= new Error('Required option db is missing');
		that.log.error(logPrefix + err.message);
		throw err;
	}
}

/**
 * Gets distinct data values from speciefied field for all users
 *
 * @param str fieldName - the name of the field
 * @param func cb(err, result) - an array with values liek ['value1', 'value2']
 */
Users.prototype.getFieldData = function (fieldName, cb) {
	const	that	= this,
		sql	= 'SELECT DISTINCT d.data FROM user_users_data d JOIN user_data_fields f ON d.fieldUuid = f.uuid WHERE f.name = "' + fieldName + '"';

	that.db.query(sql, function (err, rows) {
		const	result	= [];

		if (err) return cb(err);

		for (let row of rows) {
			result.push(row.data);
		}

		cb(err, result);
	});
};

Users.prototype.get = function (cb) {
	const	logPrefix = topLogPrefix + ' get() - ',
		dbFields	= [],
		tasks	= [],
		that	= this;

	let	sqlWhere	= '',
		totalElements,
		result;

	// Build where-statement
	tasks.push(function (cb) {
		if (that.matchExistingFields !== undefined) {
			sqlWhere	+= 'AND uuid IN (\n';
			sqlWhere	+= 'SELECT DISTINCT userUuid FROM user_users_data WHERE fieldUuid IN (\n';
			sqlWhere	+= 'SELECT uuid FROM user_data_fields WHERE\n';

			for (let i = 0; that.matchExistingFields[i] !== undefined; i ++) {
				sqlWhere += 'name = ? OR ';
				dbFields.push(that.matchExistingFields[i]);
			}

			sqlWhere	= sqlWhere.substring(0, sqlWhere.length - 4) + '))\n';
		}

		if (that.matchAllFields !== undefined) {
			for (const field in that.matchAllFields) {
				sqlWhere	+= 'AND uuid IN (SELECT userUuid FROM user_users_data WHERE data = ?\n'
					+	' AND fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?))';
				dbFields.push(that.matchAllFields[field]);
				dbFields.push(field);
			}
		}

		if (that.matchAllFieldsQ !== undefined) {
			for (const field in that.matchAllFieldsQ) {
				sqlWhere	+= 'AND uuid IN (SELECT userUuid FROM user_users_data WHERE data LIKE ?\n'
					+	' AND fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?))';
				dbFields.push('%' + that.matchAllFieldsQ[field] + '%');
				dbFields.push(field);
			}
		}

		if (that.q !== undefined) {
			sqlWhere += ' AND (\n';
			sqlWhere += '   uuid IN (SELECT userUuid FROM user_users_data WHERE data LIKE ?)\n';
			sqlWhere += '   OR username LIKE ?\n';
			sqlWhere += ')\n';
			dbFields.push('%' + that.q + '%');
			dbFields.push('%' + that.q + '%');
		}

		if (that.uuids !== undefined) {
			if ( ! Array.isArray(that.uuids)) {
				that.uuids	= [that.uuids];
			}

			if (that.uuids.length === 0) {
				sqlWhere += ' AND 1 = 2\n';
				return cb();
			}

			sqlWhere += ' AND uuid IN (';

			for (let i = 0; that.uuids[i] !== undefined; i ++) {
				if (that.lUtils.uuidToBuffer(that.uuids[i]) === false) {
					that.log.warn(logPrefix  + 'Invalid field uuid, skipping');
					continue;
				}

				sqlWhere += '?,';
				dbFields.push(that.lUtils.uuidToBuffer(that.uuids[i]));
			}

			sqlWhere = sqlWhere.substring(0, sqlWhere.length - 1) + ')\n';
		}

		cb();
	});

	tasks.push(function (cb) {

		let	sql = 'SELECT user_users.uuid as uuid, user_users.username as username',
			allowedSortables = ['uuid', 'username'];

		// SORT ORDERING
		if (that.order !== undefined && typeof that.order === 'object') {
			if (Array.isArray(that.returnFields)) allowedSortables = allowedSortables.concat(that.returnFields);

			if (that.order.by !== undefined && allowedSortables.includes(that.order.by)) {
				if (that.order.by !== 'uuid' && that.order.by !== 'username')  {
					sql += ', group_concat(user_users_data.data) as ' + that.order.by + ' FROM user_users ';
					sql += 'LEFT JOIN user_users_data on (user_users_data.fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?) AND user_users_data.userUuid = user_users.uuid) ';
					sql += 'WHERE 1';
					dbFields.push(that.order.by);
				} else {
					sql += ' FROM user_users WHERE 1';
				}

				sql += sqlWhere;
				sql += ' GROUP BY uuid';
				sql += ' ORDER BY ' + that.order.by;

				if (that.order.direction === undefined ||
					that.order.direction !== undefined &&
					that.order.direction.toUpperCase() !== 'DESC') {
					sql += ' ASC';
				} else {
					sql += ' DESC';
				}
			} else {
				return cb(new Error('The sorting column did not exist in the \'returnFields\' array'));
			}
		} else {
			sql += ' FROM user_users WHERE 1 ' + sqlWhere;
		}

		if (that.limit !== undefined && ! isNaN(parseInt(that.limit))) {
			sql += ' LIMIT ' + parseInt(that.limit);

			if (that.offset !== undefined && ! isNaN(parseInt(that.offset))) {
				sql += ' OFFSET ' + parseInt(that.offset);
			}
		}

		that.db.query(sql, dbFields, function (err, rows) {
			if (err) return cb(err);

			result	= [];

			for (let i = 0; rows[i] !== undefined; i ++) {
				const	user	= {};

				user.uuid	= that.lUtils.formatUuid(rows[i].uuid);
				user.username	= rows[i].username;

				result.push(user);
			}

			if (that.returnFields !== undefined && ! Array.isArray(that.returnFields)) {
				that.returnFields = [that.returnFields];
			}

			// Fetch field data for users, if requested
			if (that.returnFields !== undefined && that.returnFields.length > 0) {
				const subTasks = [];

				for (let u of result) {
					subTasks.push(function (cb) {
						let	subFields	= [],
							sql = 'SELECT uf.uuid AS fieldUuid,\n' +
							'uf.name AS fieldName,\n' +
							'ud.data AS fieldData,\n' +
							'ud.userUuid AS userUuid\n' +
							'FROM\n' +
								'user_data_fields uf\n' +
									'LEFT JOIN user_users_data ud ON ud.fieldUuid = uf.uuid\n' +
								'WHERE uf.name IN (';

						for (let fn of that.returnFields) {
							sql += '?,';
							subFields.push(fn);
						}

						sql	= sql.substring(0, sql.length - 1);

						sql += ') AND ud.userUuid = ?';

						if (that.lUtils.uuidToBuffer(u.uuid) === false) {
							that.log.warn(logPrefix + 'Inavlid user uuid, skipping');
							return cb();
						}

						subFields.push(that.lUtils.uuidToBuffer(u.uuid));

						that.db.query(sql, subFields, function (err, rows) {
							if (err) return cb(err);

							for (let i = 0; rows[i] !== undefined; i ++) {
								const	row	= rows[i];

								if (row.fieldUuid) {
									if (u[row.fieldName] === undefined) {
										u[row.fieldName] = [];
									}

									u[row.fieldName].push(row.fieldData);
								}
							}

							cb();
						});
					});
				}

				async.parallel(subTasks, cb);
			} else {
				cb(err);
			}
		});
	});

	tasks.push(function (cb) {
		const	sql	= 'SELECT COUNT(*) AS totalElements FROM user_users WHERE 1 ' + sqlWhere;

		that.db.query(sql, dbFields, function (err, rows) {
			if (err) return cb(err);

			totalElements	= rows[0].totalElements;

			cb(err);
		});
	});

	async.parallel(tasks, function (err) {
		cb(err, result, totalElements);
	});
};

exports = module.exports = Users;<commit_msg>fixed some bugs<commit_after>'use strict';

const	topLogPrefix = 'larvituser: users.js ',
	LUtils	= require('larvitutils'),
	async	= require('async');

function Users(options) {
	const	logPrefix	= topLogPrefix + 'Users() - ',
		that	= this;

	that.options	= options || {};

	if ( ! that.options.log) {
		const	tmpLUtils	= new LUtils();
		options.log	= new tmpLUtils.Log();
	}

	that.options	= options;

	for (const key of Object.keys(options)) {
		that[key]	= options[key];
	}

	that.lUtils	= new LUtils({'log': that.log});

	if ( ! that.db) {
		const	err	= new Error('Required option db is missing');
		that.log.error(logPrefix + err.message);
		throw err;
	}
}

/**
 * Gets distinct data values from speciefied field for all users
 *
 * @param str fieldName - the name of the field
 * @param func cb(err, result) - an array with values liek ['value1', 'value2']
 */
Users.prototype.getFieldData = function (fieldName, cb) {
	const	that	= this,
		sql	= 'SELECT DISTINCT d.data FROM user_users_data d JOIN user_data_fields f ON d.fieldUuid = f.uuid WHERE f.name = "' + fieldName + '"';

	that.db.query(sql, function (err, rows) {
		const	result	= [];

		if (err) return cb(err);

		for (let row of rows) {
			result.push(row.data);
		}

		cb(err, result);
	});
};

Users.prototype.get = function (cb) {
	const	logPrefix = topLogPrefix + ' get() - ',
		dbFields	= [],
		tasks	= [],
		that	= this;

	let	sqlWhere	= '',
		totalElements,
		result;

	// Build where-statement
	tasks.push(function (cb) {
		if (that.matchExistingFields !== undefined) {
			sqlWhere	+= 'AND uuid IN (\n';
			sqlWhere	+= 'SELECT DISTINCT userUuid FROM user_users_data WHERE fieldUuid IN (\n';
			sqlWhere	+= 'SELECT uuid FROM user_data_fields WHERE\n';

			for (let i = 0; that.matchExistingFields[i] !== undefined; i ++) {
				sqlWhere += 'name = ? OR ';
				dbFields.push(that.matchExistingFields[i]);
			}

			sqlWhere	= sqlWhere.substring(0, sqlWhere.length - 4) + '))\n';
		}

		if (that.matchAllFields !== undefined) {
			for (const field in that.matchAllFields) {
				sqlWhere	+= 'AND uuid IN (SELECT userUuid FROM user_users_data WHERE data = ?\n'
					+	' AND fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?))';
				dbFields.push(that.matchAllFields[field]);
				dbFields.push(field);
			}
		}

		if (that.matchAllFieldsQ !== undefined) {
			for (const field in that.matchAllFieldsQ) {
				sqlWhere	+= 'AND uuid IN (SELECT userUuid FROM user_users_data WHERE data LIKE ?\n'
					+	' AND fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?))';
				dbFields.push('%' + that.matchAllFieldsQ[field] + '%');
				dbFields.push(field);
			}
		}

		if (that.q !== undefined) {
			sqlWhere += ' AND (\n';
			sqlWhere += '   uuid IN (SELECT userUuid FROM user_users_data WHERE data LIKE ?)\n';
			sqlWhere += '   OR username LIKE ?\n';
			sqlWhere += ')\n';
			dbFields.push('%' + that.q + '%');
			dbFields.push('%' + that.q + '%');
		}

		if (that.uuids !== undefined) {
			if ( ! Array.isArray(that.uuids)) {
				that.uuids	= [that.uuids];
			}

			if (that.uuids.length === 0) {
				sqlWhere += ' AND 1 = 2\n';
				return cb();
			}

			sqlWhere += ' AND uuid IN (';

			for (let i = 0; that.uuids[i] !== undefined; i ++) {
				if (that.lUtils.uuidToBuffer(that.uuids[i]) === false) {
					that.log.warn(logPrefix  + 'Invalid field uuid, skipping');
					continue;
				}

				sqlWhere += '?,';
				dbFields.push(that.lUtils.uuidToBuffer(that.uuids[i]));
			}

			sqlWhere = sqlWhere.substring(0, sqlWhere.length - 1) + ')\n';
		}

		cb();
	});

	tasks.push(function (cb) {

		let	sql = 'SELECT user_users.uuid as uuid, user_users.username as username',
			allowedSortables = ['uuid', 'username'];

		if (that.returnFields !== undefined && ! Array.isArray(that.returnFields)) {
			that.returnFields = [that.returnFields];
		}

		// SORT ORDERING
		if (that.order !== undefined && typeof that.order === 'object') {

			if (Array.isArray(that.returnFields)) allowedSortables = allowedSortables.concat(that.returnFields);

			if (that.order.by !== undefined && allowedSortables.includes(that.order.by)) {
				if (that.order.by !== 'uuid' && that.order.by !== 'username')  {
					sql += ', group_concat(user_users_data.data) as ' + that.order.by + ' FROM user_users ';
					sql += 'LEFT JOIN user_users_data on (user_users_data.fieldUuid = (SELECT uuid FROM user_data_fields WHERE name = ?) AND user_users_data.userUuid = user_users.uuid) ';
					sql += 'WHERE 1 ';
					dbFields.push(that.order.by);
				} else {
					sql += ' FROM user_users WHERE 1 ';
				}

				sql += sqlWhere;
				sql += ' GROUP BY uuid';
				sql += ' ORDER BY ' + that.order.by;

				if (that.order.direction === undefined ||
					that.order.direction !== undefined &&
					that.order.direction.toUpperCase() !== 'DESC') {
					sql += ' ASC';
				} else {
					sql += ' DESC';
				}
			} else {
				return cb(new Error('The sorting column did not exist in the \'returnFields\' array'));
			}
		} else {
			sql += ' FROM user_users WHERE 1 ' + sqlWhere;
		}

		if (that.limit !== undefined && ! isNaN(parseInt(that.limit))) {
			sql += ' LIMIT ' + parseInt(that.limit);

			if (that.offset !== undefined && ! isNaN(parseInt(that.offset))) {
				sql += ' OFFSET ' + parseInt(that.offset);
			}
		}

		that.db.query(sql, dbFields, function (err, rows) {
			if (err) return cb(err);

			result	= [];

			for (let i = 0; rows[i] !== undefined; i ++) {
				const	user	= {};

				user.uuid	= that.lUtils.formatUuid(rows[i].uuid);
				user.username	= rows[i].username;

				result.push(user);
			}

			// Fetch field data for users, if requested
			if (that.returnFields !== undefined && that.returnFields.length > 0) {
				const subTasks = [];

				for (let u of result) {
					subTasks.push(function (cb) {
						let	subFields	= [],
							sql = 'SELECT uf.uuid AS fieldUuid,\n' +
							'uf.name AS fieldName,\n' +
							'ud.data AS fieldData,\n' +
							'ud.userUuid AS userUuid\n' +
							'FROM\n' +
								'user_data_fields uf\n' +
									'LEFT JOIN user_users_data ud ON ud.fieldUuid = uf.uuid\n' +
								'WHERE uf.name IN (';

						for (let fn of that.returnFields) {
							sql += '?,';
							subFields.push(fn);
						}

						sql	= sql.substring(0, sql.length - 1);

						sql += ') AND ud.userUuid = ?';

						if (that.lUtils.uuidToBuffer(u.uuid) === false) {
							that.log.warn(logPrefix + 'Inavlid user uuid, skipping');
							return cb();
						}

						subFields.push(that.lUtils.uuidToBuffer(u.uuid));

						that.db.query(sql, subFields, function (err, rows) {
							if (err) return cb(err);

							for (let i = 0; rows[i] !== undefined; i ++) {
								const	row	= rows[i];

								if (row.fieldUuid) {
									if (u[row.fieldName] === undefined) {
										u[row.fieldName] = [];
									}

									u[row.fieldName].push(row.fieldData);
								}
							}

							cb();
						});
					});
				}

				async.parallel(subTasks, cb);
			} else {
				cb(err);
			}
		});
	});

	tasks.push(function (cb) {
		const	sql	= 'SELECT COUNT(*) AS totalElements FROM user_users WHERE 1 ' + sqlWhere;

		that.db.query(sql, dbFields, function (err, rows) {
			if (err) return cb(err);

			totalElements	= rows[0].totalElements;

			cb(err);
		});
	});

	async.parallel(tasks, function (err) {
		cb(err, result, totalElements);
	});
};

exports = module.exports = Users;<|endoftext|>


<commit_before>var _ = require('lodash');
var yaml = require('js-yaml');
var fs = require('fs');
var moment = require('moment-timezone');
var crypto = require('crypto');
var markdown = require('markdown-it')({
  html: true,
  linkify: true,
  typographer: true
}).use(require('markdown-it-attrs'));
var nunjucks = require('nunjucks');

var loadedResources = {};
var loadedMarkdowns = {};
var app;

function timeProperties(items, properties) {
  items.forEach((item) => properties.forEach((prop) => item[prop] = moment.tz(item[prop], 'Europe/London')));
}

function markdownProperties(items, properties) {
  items.forEach((item) => properties.forEach((prop) => item[prop] = nunjucks.runtime.markSafe(markdown.render(item[prop]))));
}

exports.init = function init(a) {
  app = a;
};

var assetsFile;

try {
  assetsFile = require('./assets/dist/rev-manifest.json');
} catch (e) {
  assetsFile = { };
}

exports.asset = function (asset, prefix) {
  if (prefix == null) {
    prefix = '/assets/'
  }

  if (_.has(assetsFile, asset)) {
    asset = assetsFile[asset];
  }

  return prefix + asset;
};

var loadedAssets = { };

exports.loadAsset = function loadAsset(assetName) {
  if ((!loadedAssets[assetName]) || (app.settings.env == 'development')) {
    loadedAssets[assetName] = fs.readFileSync(exports.asset(assetName, 'assets/dist/'));
  }

  return loadedAssets[assetName];
}

exports.loadResource = function loadResource(resourceName) {
  if ((!loadedResources[resourceName]) || (app.settings.env == 'development')) {
    var loadedResource = yaml.safeLoad(fs.readFileSync('./resources/' + resourceName + '.yml'))[resourceName];

    switch (resourceName) {
      case 'faqs':
        markdownProperties(loadedResource, ['answer']);
        break;
      case 'prizes':
        _.forOwn(loadedResource, (item) => markdownProperties(item, ['description', 'prize']));
        break;
      case 'workshops':
        markdownProperties(loadedResource, ['description']);
        timeProperties(loadedResource, ['time'])
        break;
      case 'schedule':
        timeProperties(loadedResource, ['time']);
        loadedResource = {
          saturday: loadedResource.filter((event) => event.time.date() == 30),
          sunday: loadedResource.filter((event) => event.time.date() == 31)
        };
    }

    loadedResources[resourceName] = loadedResource;
  }

  return loadedResources[resourceName];
}

exports.loadMarkdown = function loadMarkdown(markdownName) {
  if ((!loadedMarkdowns[markdownName]) || (app.settings.env == 'development')) {
    var loadedMarkdown = nunjucks.runtime.markSafe(markdown.render(fs.readFileSync('./resources/' + markdownName + '.md', 'utf8')));
    loadedMarkdowns[markdownName] = loadedMarkdown;
  }

  return loadedMarkdowns[markdownName];
};

var publicId = process.env.APP_ID || crypto.randomBytes(10).toString('hex');

exports.getPublicId = function () {
  return publicId;
};
<commit_msg>Fix publicID generation<commit_after>var _ = require('lodash');
var yaml = require('js-yaml');
var fs = require('fs');
var moment = require('moment-timezone');
var crypto = require('crypto');
var markdown = require('markdown-it')({
  html: true,
  linkify: true,
  typographer: true
}).use(require('markdown-it-attrs'));
var nunjucks = require('nunjucks');

var loadedResources = {};
var loadedMarkdowns = {};
var app;

function timeProperties(items, properties) {
  items.forEach((item) => properties.forEach((prop) => item[prop] = moment.tz(item[prop], 'Europe/London')));
}

function markdownProperties(items, properties) {
  items.forEach((item) => properties.forEach((prop) => item[prop] = nunjucks.runtime.markSafe(markdown.render(item[prop]))));
}

exports.init = function init(a) {
  app = a;
};

var assetsFile;

try {
  assetsFile = require('./assets/dist/rev-manifest.json');
} catch (e) {
  assetsFile = { };
}

exports.asset = function (asset, prefix) {
  if (prefix == null) {
    prefix = '/assets/'
  }

  if (_.has(assetsFile, asset)) {
    asset = assetsFile[asset];
  }

  return prefix + asset;
};

var loadedAssets = { };

exports.loadAsset = function loadAsset(assetName) {
  if ((!loadedAssets[assetName]) || (app.settings.env == 'development')) {
    loadedAssets[assetName] = fs.readFileSync(exports.asset(assetName, 'assets/dist/'));
  }

  return loadedAssets[assetName];
}

exports.loadResource = function loadResource(resourceName) {
  if ((!loadedResources[resourceName]) || (app.settings.env == 'development')) {
    var loadedResource = yaml.safeLoad(fs.readFileSync('./resources/' + resourceName + '.yml'))[resourceName];

    switch (resourceName) {
      case 'faqs':
        markdownProperties(loadedResource, ['answer']);
        break;
      case 'prizes':
        _.forOwn(loadedResource, (item) => markdownProperties(item, ['description', 'prize']));
        break;
      case 'workshops':
        markdownProperties(loadedResource, ['description']);
        timeProperties(loadedResource, ['time'])
        break;
      case 'schedule':
        timeProperties(loadedResource, ['time']);
        loadedResource = {
          saturday: loadedResource.filter((event) => event.time.date() == 30),
          sunday: loadedResource.filter((event) => event.time.date() == 31)
        };
    }

    loadedResources[resourceName] = loadedResource;
  }

  return loadedResources[resourceName];
}

exports.loadMarkdown = function loadMarkdown(markdownName) {
  if ((!loadedMarkdowns[markdownName]) || (app.settings.env == 'development')) {
    var loadedMarkdown = nunjucks.runtime.markSafe(markdown.render(fs.readFileSync('./resources/' + markdownName + '.md', 'utf8')));
    loadedMarkdowns[markdownName] = loadedMarkdown;
  }

  return loadedMarkdowns[markdownName];
};

var publicId = crypto.randomBytes(12).toString('hex');

exports.getPublicId = function () {
  return publicId;
};
<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var fs 	 = require('fs');
var _  = require('lodash-node');

//
// determine bower components home dir, maybe need to get dir name from '.bowerrc'
//
function componentsHome(workdir) {
	var bowerrc = path.join(workdir, '.bowerrc'),
		defaultHome = path.join(workdir, 'bower_components');
	return (fs.existsSync(bowerrc) && require(bowerrc).directory) || defaultHome;
}

//
// extract component name from inputting rawname
//
function componentName(rawname) {
	var name = rawname.split(':')[0],
		index = name.replace('\\', '/').indexOf('/');
	return (index > 0) ? name.substring(0, index) : name;
}

//
// get bower components names
//
function componentNames(workdir) {
	workdir = workdir || process.cwd();
	var bowerJson = require(path.join(workdir, 'bower.json'));
	return _(Object.keys(bowerJson.dependencies || {}))
		.union(Object.keys(bowerJson.devDependencies || {}))
		.value();
}
exports.componentNames = componentNames;

//
// resolve and return entry file's full path for specified component
//
function resolve(name, workdir) {
	workdir = workdir || process.cwd();
	var compName = componentName(name),
		subname = name.substring(compName.length + 1),
		basedir = path.join(componentsHome(workdir), compName),
		bowerJson = require(path.join(basedir, 'bower.json'));

	if (subname && subname.length > 0) {
		return path.join(basedir, subname);
	} else {
		var mainfile = Array.isArray(bowerJson.main)
			? bowerJson.main.filter(function(file) { return /\.js$/.test(file); })[0]
			: bowerJson.main;

		if (mainfile) {
			return path.join(basedir, mainfile);
		}	else {
			if (fs.existsSync(path.join(basedir, "index.js"))) {
				return path.join(basedir, "index.js");
			} else {
				return path.join(basedir, compName);
			}
		}
	}
}
exports.resolve = resolve;
<commit_msg>enhance resolving to try more<commit_after>'use strict';

var path = require('path');
var fs 	 = require('fs');
var _  = require('lodash-node');

//
// determine bower components home dir, maybe need to get dir name from '.bowerrc'
//
function componentsHome(workdir) {
	var bowerrc = path.join(workdir, '.bowerrc'),
		defaultHome = path.join(workdir, 'bower_components');
	return (fs.existsSync(bowerrc) && require(bowerrc).directory) || defaultHome;
}

//
// extract component name from inputting rawname
//
function componentName(rawname) {
	var name = rawname.split(':')[0],
		index = name.replace('\\', '/').indexOf('/');
	return (index > 0) ? name.substring(0, index) : name;
}

//
// get bower components names
//
function componentNames(workdir) {
	workdir = workdir || process.cwd();
	var bowerJson = require(path.join(workdir, 'bower.json'));
	return _(Object.keys(bowerJson.dependencies || {}))
		.union(Object.keys(bowerJson.devDependencies || {}))
		.value();
}
exports.componentNames = componentNames;

//
// resolve and return entry file's full path for specified component
//
function resolve(name, workdir) {
	workdir = workdir || process.cwd();
	var compName = componentName(name),
		subname = name.substring(compName.length + 1),
		basedir = path.join(componentsHome(workdir), compName),
		bowerJson = require(path.join(basedir, 'bower.json'));

	var mainfile = Array.isArray(bowerJson.main)
		? bowerJson.main.filter(function(file) { return /\.js$/.test(file); })[0]
		: bowerJson.main;

	if (subname && subname.length > 0) {
		var subpath = mainfile && path.join(basedir, mainfile, '..', subname);
		if (subpath && (fs.existsSync(subpath) || fs.existsSync(subpath + '.js'))) {
			return path.join(basedir, mainfile, '..', subname);
		} else {
			return path.join(basedir, subname);
		}
	} else {
		if (mainfile) {
			return path.join(basedir, mainfile);
		}	else {
			if (fs.existsSync(path.join(basedir, "index.js"))) {
				return path.join(basedir, "index.js");
			} else {
				return path.join(basedir, compName);
			}
		}
	}
}
exports.resolve = resolve;
<|endoftext|>


<commit_before>import React from 'react';

import {
  Dimensions,
} from 'react-native';

const getNavigationDelegate = (component) => {
  return component.navigationDelegate ||
    (component.type && component.type.navigationDelegate)
}

const getOrientation = () => {
  const { width, height } = Dimensions.get('window');

  return height > width ? 'PORTRAIT' : 'LANDSCAPE';
}

const replaceInstanceEventedProps =
  (reactElement, eventedProps, events = [], route, navigationContext) => {

  eventedProps.forEach((eventedProp) => {
    if (React.isValidElement(reactElement) && reactElement.props[eventedProp]) {
      const event = reactElement.props[eventedProp]();

      if (typeof event === 'string') {
        if (events.indexOf(event) < 0) {
          events.push(event)
        }

        reactElement = React.cloneElement(reactElement, {
          [eventedProp]: (e) => navigationContext.emit(event, {route, e}),
        })
      }
    }
  })

  return {reactElement, events};
}

export {
  getNavigationDelegate,
  getOrientation,
  replaceInstanceEventedProps,
}
<commit_msg>Small fix for mobx-react users<commit_after>import React from 'react';

import {
  Dimensions,
} from 'react-native';

const getNavigationDelegate = (component) => {
  return (component.wrappedComponent && component.wrappedComponent.navigationDelegate) ||
    component.navigationDelegate ||
    (component.type && component.type.navigationDelegate)
}

const getOrientation = () => {
  const { width, height } = Dimensions.get('window');

  return height > width ? 'PORTRAIT' : 'LANDSCAPE';
}

const replaceInstanceEventedProps =
  (reactElement, eventedProps, events = [], route, navigationContext) => {

  eventedProps.forEach((eventedProp) => {
    if (React.isValidElement(reactElement) && reactElement.props[eventedProp]) {
      const event = reactElement.props[eventedProp]();

      if (typeof event === 'string') {
        if (events.indexOf(event) < 0) {
          events.push(event)
        }

        reactElement = React.cloneElement(reactElement, {
          [eventedProp]: (e) => navigationContext.emit(event, {route, e}),
        })
      }
    }
  })

  return {reactElement, events};
}

export {
  getNavigationDelegate,
  getOrientation,
  replaceInstanceEventedProps,
}
<|endoftext|>


<commit_before>var express = require('express');
var cors = require('cors');
var app = express();

var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({level: 'info'}),
    new (winston.transports.File)({
      filename: 'server-debug.log',
      level: 'debug'
    })
  ]
});

var personSearch = require('./person-search');
var skillsSearch = require('./skills-search');

exports.createServer = function (port) {
  logger.info('Starting server on port', port);

  app.get('/', cors(), function (req, res) {
    res.status(501);
    res.set('Content-Type', 'application/json');
    res.send(JSON.stringify({message: 'not implemented'}));
  });

  app.get('/persons', cors(), function (req, res) {
    var skills = req.query.skills.split(',');
    if (skills) {
      personSearch.findPeopleWithSkills(skills, isTrue(req.query.infer), function (err, result) {
        res.status(200);
        res.set('Content-Type', 'application/json');
        res.send(JSON.stringify(result));
        logger.debug('Returning people with skills "%s" (infer? %s)', skills, isTrue(req.query.infer));
      });
    } else {
      res.status(200);
      res.set('Content-Type', 'application/json');
      res.send(JSON.stringify(personSearch.allPersons));
      logger.debug('Returning all persons');
    }
  });

  app.get('/persons/:person', cors(), function (req, res) {
    res.set('Content-Type', 'application/json');
    var id = req.params.person;
    personSearch.findPerson(id, true, function (err, person) {
      if (person) {
        res.status(200);
        res.send(JSON.stringify(person));
        logger.debug('Returning person with id %s', id);
      } else {
        res.status(404);
        res.send(JSON.stringify({error: 'No person with id "' + id + '" found.'}));
        logger.debug('No person with id %s found', id);
      }
    });
  });

  app.get('/skills', cors(), function (req, res) {
    skillsSearch.skills(function (err, skills) {
      res.status(200);
      res.send(JSON.stringify(skills));
      logger.debug('Returned skills %s', skills);
    });
  });

  var server = app.listen(port, function () {
    var host = server.address().address;
    var port = server.address().port;

    logger.debug('Server running at http://%s:%s', host, port);
  });

  return server;
};

function isTrue(param) {
  return param || param === "true";
}<commit_msg>Fixed splitting problem<commit_after>var express = require('express');
var cors = require('cors');
var app = express();

var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({level: 'info'}),
    new (winston.transports.File)({
      filename: 'server-debug.log',
      level: 'debug'
    })
  ]
});

var personSearch = require('./person-search');
var skillsSearch = require('./skills-search');

exports.createServer = function (port) {
  logger.info('Starting server on port', port);

  app.get('/', cors(), function (req, res) {
    res.status(501);
    res.set('Content-Type', 'application/json');
    res.send(JSON.stringify({message: 'not implemented'}));
  });

  app.get('/persons', cors(), function (req, res) {
    var skills = req.query.skills;
    if (skills) {
      var splitSkills = req.query.skills.split(',');
      personSearch.findPeopleWithSkills(splitSkills, isTrue(req.query.infer), function (err, result) {
        res.status(200);
        res.set('Content-Type', 'application/json');
        res.send(JSON.stringify(result));
        logger.debug('Returning people with skills "%s" (infer? %s)', splitSkills, isTrue(req.query.infer));
      });
    } else {
      res.status(200);
      res.set('Content-Type', 'application/json');
      res.send(JSON.stringify(personSearch.allPersons));
      logger.debug('Returning all persons');
    }
  });

  app.get('/persons/:person', cors(), function (req, res) {
    res.set('Content-Type', 'application/json');
    var id = req.params.person;
    personSearch.findPerson(id, true, function (err, person) {
      if (person) {
        res.status(200);
        res.send(JSON.stringify(person));
        logger.debug('Returning person with id %s', id);
      } else {
        res.status(404);
        res.send(JSON.stringify({error: 'No person with id "' + id + '" found.'}));
        logger.debug('No person with id %s found', id);
      }
    });
  });

  app.get('/skills', cors(), function (req, res) {
    skillsSearch.skills(function (err, skills) {
      res.status(200);
      res.send(JSON.stringify(skills));
      logger.debug('Returned skills %s', skills);
    });
  });

  var server = app.listen(port, function () {
    var host = server.address().address;
    var port = server.address().port;

    logger.debug('Server running at http://%s:%s', host, port);
  });

  return server;
};

function isTrue(param) {
  return param || param === "true";
}<|endoftext|>


<commit_before>module.exports = which
which.sync = whichSync

var path = require('path')
var COLON = process.platform === 'win32' ? ';' : ':'
var isExe
var fs = require('fs')
var isAbsolute = require('is-absolute')

if (process.platform == 'win32') {
  // On windows, there is no good way to check that a file is executable
  isExe = function isExe () { return true }
} else {
  isExe = function isExe (mod, uid, gid) {
    var ret = (mod & 0001)
        || (mod & 0010) && process.getgid && gid === process.getgid()
        || (mod & 0100) && process.getuid && uid === process.getuid()
        || (mod & 0110) && process.getuid && 0   === process.getuid()
    return ret
  }
}

function which (cmd, opt, cb) {
  if (typeof opt === 'function') {
    cb = opt
    opt = {}
  }

  var colon = opt.colon || COLON
  var pathEnv = (opt.path || process.env.PATH || '').split(colon)
  var pathExt = ['']

  if (process.platform === 'win32') {
    pathEnv.push(process.cwd())
    pathExt = opt.pathExt || (process.env.PATHEXT || '.EXE').split(colon)
    if (cmd.indexOf('.') !== -1)
      pathExt.unshift('')
  }

  // If it's absolute, then we don't bother searching the pathenv.
  // just check the file itself, and that's it.
  if (isAbsolute(cmd))
    pathEnv = ['']

  ;(function F (i, l) {
    if (i === l) return cb(new Error('not found: '+cmd))
    var p = path.resolve(pathEnv[i], cmd)
    ;(function E (ii, ll) {
      if (ii === ll) return F(i + 1, l)
      var ext = pathExt[ii]
      fs.stat(p + ext, function (er, stat) {
        if (!er &&
            stat.isFile() &&
            isExe(stat.mode, stat.uid, stat.gid)) {
          return cb(null, p + ext)
        }
        return E(ii + 1, ll)
      })
    })(0, pathExt.length)
  })(0, pathEnv.length)
}

function whichSync (cmd, opt) {
  if (!opt)
    opt = {}

  var colon = opt.colon || COLON

  var pathEnv = (opt.path || process.env.PATH || '').split(colon)
  var pathExt = ['']

  if (process.platform === 'win32') {
    pathEnv.push(process.cwd())
    pathExt = (opt.pathExt || process.env.PATHEXT || '.EXE').split(colon)
    if (cmd.indexOf('.') !== -1) {
      pathExt.unshift('')
    }
  }

  // If it's absolute, then we don't bother searching the pathenv.
  // just check the file itself, and that's it.
  if (isAbsolute(cmd))
    pathEnv = ['']

  for (var i = 0, l = pathEnv.length; i < l; i ++) {
    var p = path.join(pathEnv[i], cmd)
    for (var j = 0, ll = pathExt.length; j < ll; j ++) {
      var cur = p + pathExt[j]
      var stat
      try {
        stat = fs.statSync(cur)
        if (stat.isFile() && isExe(stat.mode, stat.uid, stat.gid))
          return cur
      } catch (ex) {}
    }
  }

  throw new Error('not found: '+cmd)
}
<commit_msg>Handle lower-case 'env.Path' on Windows<commit_after>module.exports = which
which.sync = whichSync

var path = require('path')
var COLON = process.platform === 'win32' ? ';' : ':'
var isExe
var fs = require('fs')
var isAbsolute = require('is-absolute')

if (process.platform == 'win32') {
  // On windows, there is no good way to check that a file is executable
  isExe = function isExe () { return true }
} else {
  isExe = function isExe (mod, uid, gid) {
    var ret = (mod & 0001)
        || (mod & 0010) && process.getgid && gid === process.getgid()
        || (mod & 0100) && process.getuid && uid === process.getuid()
        || (mod & 0110) && process.getuid && 0   === process.getuid()
    return ret
  }
}

function which (cmd, opt, cb) {
  if (typeof opt === 'function') {
    cb = opt
    opt = {}
  }

  var colon = opt.colon || COLON
  var pathEnv = opt.path || process.env.PATH || ''
  var pathExt = ['']

  // On windows, env.Path is common.
  if (process.platform === 'win32' && !pathEnv) {
    var k = Object.keys(process.env)
    for (var p = 0; p < k.length; p++) {
      if (p.toLowerCase() === 'path') {
        pathEnv = process.env[p]
        break
      }
    }
  }

  pathEnv = pathEnv.split(colon)

  if (process.platform === 'win32') {
    pathEnv.push(process.cwd())
    pathExt = opt.pathExt || (process.env.PATHEXT || '.EXE').split(colon)
    if (cmd.indexOf('.') !== -1)
      pathExt.unshift('')
  }

  // If it's absolute, then we don't bother searching the pathenv.
  // just check the file itself, and that's it.
  if (isAbsolute(cmd))
    pathEnv = ['']

  ;(function F (i, l) {
    if (i === l) return cb(new Error('not found: '+cmd))
    var p = path.resolve(pathEnv[i], cmd)
    ;(function E (ii, ll) {
      if (ii === ll) return F(i + 1, l)
      var ext = pathExt[ii]
      fs.stat(p + ext, function (er, stat) {
        if (!er &&
            stat.isFile() &&
            isExe(stat.mode, stat.uid, stat.gid)) {
          return cb(null, p + ext)
        }
        return E(ii + 1, ll)
      })
    })(0, pathExt.length)
  })(0, pathEnv.length)
}

function whichSync (cmd, opt) {
  if (!opt)
    opt = {}

  var colon = opt.colon || COLON

  var pathEnv = opt.path || process.env.PATH || ''
  var pathExt = ['']

  // On windows, env.Path is common.
  if (process.platform === 'win32' && !pathEnv) {
    var k = Object.keys(process.env)
    for (var p = 0; p < k.length; p++) {
      if (p.toLowerCase() === 'path') {
        pathEnv = process.env[p]
        break
      }
    }
  }

  pathEnv = pathEnv.split(colon)

  if (process.platform === 'win32') {
    pathEnv.push(process.cwd())
    pathExt = (opt.pathExt || process.env.PATHEXT || '.EXE').split(colon)
    if (cmd.indexOf('.') !== -1) {
      pathExt.unshift('')
    }
  }

  // If it's absolute, then we don't bother searching the pathenv.
  // just check the file itself, and that's it.
  if (isAbsolute(cmd))
    pathEnv = ['']

  for (var i = 0, l = pathEnv.length; i < l; i ++) {
    var p = path.join(pathEnv[i], cmd)
    for (var j = 0, ll = pathExt.length; j < ll; j ++) {
      var cur = p + pathExt[j]
      var stat
      try {
        stat = fs.statSync(cur)
        if (stat.isFile() && isExe(stat.mode, stat.uid, stat.gid))
          return cur
      } catch (ex) {}
    }
  }

  throw new Error('not found: '+cmd)
}
<|endoftext|>


<commit_before>function _debug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback,sendto,jumpback) {
    //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans,sendto,jumpback);
        },
        'error': function() {
            _debug("ajax error occured");
        }
    });
}

function sendmsg_callback(ans,sendto,jumpback){
    _debug("bot resp with: " + ans);
    sendto && $(sendto).click();
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    $(jumpback).click();
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
        typeof(observer) !== 'undefined' && observer.disconnect();
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _debug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _debug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _debug(bots.length.toString()+" bots initiated");
        //set chat list update listener
        setupdater();
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _debug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && installbot(uninstalled);
}

function setupdater() {
    updater = new MutationObserver(function(mutations) {
        botupdate();
    });
    updater.observe($('#conversationContainer')[0], { childList: true});
    _debug('updater set');
}

function chatbot() {
    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //record current chat
    var activechat = $('.activeColumn');

    //reply in current chat window
    //not working now, deal with it later
    typeof(observer) !== 'undefined' && observer.disconnect();
    if ( $('.activeColumn:has(".bot.active")').length ) {
        var target = $('.activeColumn .descWrapper');
        observer = new MutationObserver(function(mutations) {
            var newmsg = target.find('.desc').text();
            if ($('#chat_chatmsglist').children().last().hasClass('chatItem you')) {
                var name = $(activechat).find('.left.name').text();
                _debug("msg from: " + name);
                if (newmsg) {
                    _debug("msg content: " + newmsg);
                    callBotAPI(newmsg,sendmsg,null,$(activechat));
                } else {
                    _debug("no msg found");
                }
            }
        });
        observer.observe(target[0], {childList: true});
    }

    //reply for red dotted item
    $('.unreadDot:visible, .unreadDotS:visible').each(function()
    {
        var receiver = $(this).parent();
        is_active = $(receiver).find(".bot").hasClass("active");
        if (!is_active) return;
        var name = $(receiver).find(".left.name").text();
        _debug("msg from: " + name);

        var newmsg = $(receiver).find('.desc').text();
        if (newmsg) {
            _debug("msg content: " + newmsg);
            callBotAPI(newmsg,sendmsg_callback,$(receiver),$(activechat));
        } else {
            _debug("no msg found");
        }
    });
}

DEBUG = true;
VERSION = "2.1.1";
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<commit_msg>for debug<commit_after>function _debug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback,sendto,jumpback) {
    //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans,sendto,jumpback);
        },
        'error': function() {
            _debug("ajax error occured");
        }
    });
}

function sendmsg_callback(ans,sendto,jumpback){
    _debug("bot resp with: " + ans);
    sendto && $(sendto).click();
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    $(jumpback).click();
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
        typeof(observer) !== 'undefined' && observer.disconnect();
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _debug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _debug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _debug(bots.length.toString()+" bots initiated");
        //set chat list update listener
        setupdater();
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _debug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && installbot(uninstalled);
}

function setupdater() {
    updater = new MutationObserver(function(mutations) {
        botupdate();
    });
    updater.observe($('#conversationContainer')[0], { childList: true});
    _debug('updater set');
}

function chatbot() {
    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //record current chat
    var activechat = $('.activeColumn');

    //reply in current chat window
    if ( $('.activeColumn:has(".bot.active")').length ) {
        var lastchat = $('#chat_chatmsglist').children().last();
            if ($(lastchat).hasClass('chatItem you')) {
            _debug("msg from: " + name);
            if (newmsg) {
                _debug("msg content: " + newmsg);
                callBotAPI(newmsg,sendmsg,activechat);
            } else {
                _debug("no msg found");
            }
        }
    }

    //reply for red dotted item
    $('.unreadDot:visible, .unreadDotS:visible').each(function()
    {
        var receiver = $(this).parent();
        is_active = $(receiver).find(".bot").hasClass("active");
        if (!is_active) return;
        var name = $(receiver).find(".left.name").text();
        _debug("msg from: " + name);

        var newmsg = $(receiver).find('.desc').text();
        if (newmsg) {
            _debug("msg content: " + newmsg);
            callBotAPI(newmsg,sendmsg_callback,$(receiver),$(activechat));
        } else {
            _debug("no msg found");
        }
    });
}

DEBUG = true;
VERSION = "2.2.0";
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<|endoftext|>


<commit_before>/**
 * XView
 *
 * Easily create hierarchical structures
 * An XView can make multiple child views, which can have multiple child views.
 * When removing a parent view, children are removed automatically.
 *
 * Easy templating
 * Add a template string to the template property, and optionally add unwrap: true
 * to avoid having Backbone wrap it in an extra tag. This can make templating
 * and styling easier.
 */

/**
 * Factory
 */
;(function(factory) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define(['underscore', 'backbone'], factory);
  }

  //CommonJS
  else if (typeof exports !== 'undefined') {
    factory(require('underscore'), require('backbone'));
  }

  //Globals
  else {
    factory(_, Backbone);
  }
}(function(_, Backbone) {

  /**
   * Main view
   */
  var XView = Backbone.View.extend({
    //Helpers functions (and properties) that are sent to the template on render
    renderHelpers: {},

    /**
     * Constructor
     *
     * @param {Object} [options]
     * @param {View} [options.parent]       Parent view (Passed automatically when using XView#addView)
     * @param {Function} [options.template] Compiled template, e.g. the result of _.template('

')
     * @param {Boolean} [options.unwrap]    Whether to use only the template, so that there isn't an extra tag around the template contents. NOTE: If using unwrap, there must be one single parent element in the template.
     */
    constructor: function(options) {
      options = options || {};

      this.parent = options.parent;
      this.children = {};

      if (options.template) this.template = options.template;
      if (options.unwrap) this.unwrap = options.unwrap;

      this.rendered = false;

      Backbone.View.prototype.constructor.call(this, options);
    },

    /**
     * Returns the data to be sent to the template.
     * Default implementation is to return the model JSON object,
     * but this method can be overridden to return any other data
     *
     * This can also be overridden with a simple object rather than function.
     *
     * @return {Object}     The data to send to the template
     */
    templateData: function() {
      return this.model ? this.model.toJSON() : {};
    },

    /**
     * Adds a child view.
     * If a selector is specified, the view will be appended to that part of the template.
     *
     * @param {String} [selector]
     * @param {Object} [options]
     * @param {Mixed} [options.id]    For fine-grained control if you need to access views after being added. Not necessary in most cases.
     * @param {View} view
     * @return {View}
     */
    addView: function(selector, options, view) {
      //Normalise arguments
      if (arguments.length == 2) { //selector, view
        view = options;
        options = {};
      } else if (arguments.length == 1) { //view
        view = selector;
        options = {};
        selector = null;
      }

      //Generate an ID if not given
      var id = options.id || _.uniqueId();

      //Add to the list of children
      var child = this.children[id] = {
        selector: selector,
        view: view
      };

      //If this view is already rendered, add the child automatically
      if (this.rendered) {
        this.renderView(id);
      }

      return view;
    },

    /**
     * Get a child view by ID
     *
     * @param {Mixed} id      The child view ID
     *
     * @return {View|Null}
     */
    getView: function(id) {
      var child = this.children[id];

      return child ? child.view : null;
    },

    /**
     * Renders the main element with the template.
     *
     * If using a template and the 'unwrap' property is true then the template HTML will not be wrapped
     * in an extra tag. This can make editing and styling templates easier.
     *
     * @return {View}     Returns self
     */
    render: function() {
      if (this.template) {
        var html = '';

        //Get data for template
        var data = _.isFunction(this.templateData) ? this.templateData() : this.templateData;

        //Add template helpers
        _.extend(data, this.renderHelpers);

        //Render HTML
        html = Backbone.$(this.template(data));

        //If using unwrap and this is an update, only replace the main element contents
        //This ensures the view that's actually in the DOM is updated
        if (this.unwrap) {
          if (!this.rendered) {
            this.setElement(html);
          } else {
            this.$el.html(html.html());
            this.delegateEvents();
          }
        } else {
          this.$el.html(html);
        }
      }

      this.renderViews();

      if (this.onRender) this.onRender();

      this.rendered = true;

      return this;
    },

    /**
     * Render all child views
     */
    renderViews: function() {
      var self = this,
          ids = _.keys(this.children);

      _.each(ids, _.bind(self.renderView, this));
    },

    /**
     * Renders a child view and adds it to it's locations in the parent element
     *
     * @param {Mixed} id    The child view ID
     */
    renderView: function(id) {
      var child = this.children[id];

      var selector = child.selector,
          view = child.view,
          $el = null;

      view.render();

      //Decide where the child view is going
      if (selector) {
        $el = this.$el.find(selector);
      } else {
        $el = this.$el;
      }

      $el.append(view.el);
    },

    /**
     * Close and remove all child views
     */
    removeViews: function() {
      var self = this,
          ids = _.keys(this.children);

      _.each(ids, _.bind(self.removeView, this));
    },

    /**
     * Close and remove a child view
     *
     * @param {Mixed} id    Child view ID
     */
    removeView: function(id) {
      var children = this.children,
          child = children[id];

      if (!child) return;

      child.view.remove();

      delete children[id];
    },

    /**
     * Closes child views then this one, removes event listeners etc.
     */
    remove: function() {
      this.removeViews();

      Backbone.View.prototype.remove.call(this);
    }
  });

  /**
   * CollectionView
   * Renders a list of ItemViews representing a collection
   */
  var CollectionView = XView.extend({
    tagName: 'ul',

    //Selector string for where items will be inserted. If not set, will default to the root element
    listSelector: null,

    //Selector string for content to show if there are no items
    fallbackSelector: null,

    //Selector string for content to show if there are no items
    loadingSelector: null,

    /**
     * Constructor
     *
     * @param {Object} options
     * @param {Collection} options.collection   Collection to render
     * @param {XView} options.itemView          Constructor (not instance) for an item view
     */
    constructor: function(options) {
      XView.prototype.constructor.call(this, options);

      options = options || {};

      if (options.collection) this.collection = options.collection;
      if (!this.collection) throw new Error('collection is required');

      if (options.itemView) this.itemView = options.itemView;
      if (!this.itemView) throw new Error('itemView is required');

      this.listenTo(this.collection, 'add', this.addItem);
      this.listenTo(this.collection, 'remove', this.removeItem);
      this.listenTo(this.collection, 'reset', this.resetItems);
      this.listenTo(this.collection, 'request', this.onRequest);
      this.listenTo(this.collection, 'sync', this.onSync);

      //Render items if already in the collection
      if (this.collection.length) this.resetItems();
    },

    render: function() {
      XView.prototype.render.call(this);

      //Show loading if collection is being fetched
      if (this.isLoading) {
        this.showLoading();

        this.hideFallback();
      }

      //Not loading; show fallback if collection is empty
      else {
        this.hideLoading();

        if (this.collection.length) {
          this.hideFallback();
        } else {
          this.showFallback();
        }
      }

      return this;
    },

    /**
     * Shows the fallback element using this.fallbackSelector
     */
    showFallback: function() {
      this.$(this.fallbackSelector).show();
    },

    /**
     * Hides the fallback element using this.fallbackSelector
     */
    hideFallback: function() {
      this.$(this.fallbackSelector).hide();
    },

    /**
     * Shows the loading element using this.loadingSelector
     */
    showLoading: function() {
      this.$(this.loadingSelector).show();
    },

    /**
     * Hides the loading element using this.loadingSelector
     */
    hideLoading: function() {
      this.$(this.loadingSelector).hide();
    },

    /**
     * @param {Model} model
     */
    addItem: function(model) {
      var view = new this.itemView({
        model: model,
        listView: this, //TODO: remove listView in favour of 'parent'?
        parent: this
      });

      this.addView(this.listSelector || null, { id: model.cid }, view);
    },

    /**
     * @param {Model} model
     */
    removeItem: function(model) {
      this.removeView(model.cid);
    },

    /**
     * Removes existing item views and adds the current collection contents
     */
    resetItems: function() {
      this.removeViews();

      this.collection.each(_.bind(this.addItem, this));

      //Show fallback if there are no items
      if (!this.collection.length) {
        this.showFallback();
      }
    },

    /**
     * Get the item container (if rendered). Uses the this.listSelector selector if available;
     * otherwise the main element is returned
     *
     * @return {jQuery}
     */
    getListEl: function() {
      if (!this.rendered) throw new Error('View has not yet been rendered');

      var $el;

      if (this.listSelector) {
        $el = this.$(this.listSelector);
      } else {
        $el = this.$el;
      }

      return $el;
    },

    /**
     * Callback for when a request has started, i.e. collection is in loading state
     */
    onRequest: function() {
      this.isLoading = true;

      //Show loading if there are no items
      if (!this.collection.length) {
        this.showLoading();
      }
    },

    /**
     * Callback for when a request has ended, i.e. collection is not in loading state
     */
    onSync: function() {
      //Hide loading
      this.isLoading = false;
      this.hideLoading();

      //Show fallback if there are no items
      if (!this.collection.length) {
        this.showFallback();
      }
    }
  });

  /**
   * Exports
   */
  XView.Collection = CollectionView;

  Backbone.XView = XView;

  //For use in NodeJS
  if (typeof module != 'undefined') module.exports = XView;

  return Backbone.XView;

}));
<commit_msg>Remove unused code<commit_after>/**
 * XView
 *
 * Easily create hierarchical structures
 * An XView can make multiple child views, which can have multiple child views.
 * When removing a parent view, children are removed automatically.
 *
 * Easy templating
 * Add a template string to the template property, and optionally add unwrap: true
 * to avoid having Backbone wrap it in an extra tag. This can make templating
 * and styling easier.
 */

/**
 * Factory
 */
;(function(factory) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define(['underscore', 'backbone'], factory);
  }

  //CommonJS
  else if (typeof exports !== 'undefined') {
    factory(require('underscore'), require('backbone'));
  }

  //Globals
  else {
    factory(_, Backbone);
  }
}(function(_, Backbone) {

  /**
   * Main view
   */
  var XView = Backbone.View.extend({
    //Helpers functions (and properties) that are sent to the template on render
    renderHelpers: {},

    /**
     * Constructor
     *
     * @param {Object} [options]
     * @param {View} [options.parent]       Parent view (Passed automatically when using XView#addView)
     * @param {Function} [options.template] Compiled template, e.g. the result of _.template('

')
     * @param {Boolean} [options.unwrap]    Whether to use only the template, so that there isn't an extra tag around the template contents. NOTE: If using unwrap, there must be one single parent element in the template.
     */
    constructor: function(options) {
      options = options || {};

      this.parent = options.parent;
      this.children = {};

      if (options.template) this.template = options.template;
      if (options.unwrap) this.unwrap = options.unwrap;

      this.rendered = false;

      Backbone.View.prototype.constructor.call(this, options);
    },

    /**
     * Returns the data to be sent to the template.
     * Default implementation is to return the model JSON object,
     * but this method can be overridden to return any other data
     *
     * This can also be overridden with a simple object rather than function.
     *
     * @return {Object}     The data to send to the template
     */
    templateData: function() {
      return this.model ? this.model.toJSON() : {};
    },

    /**
     * Adds a child view.
     * If a selector is specified, the view will be appended to that part of the template.
     *
     * @param {String} [selector]
     * @param {Object} [options]
     * @param {Mixed} [options.id]    For fine-grained control if you need to access views after being added. Not necessary in most cases.
     * @param {View} view
     * @return {View}
     */
    addView: function(selector, options, view) {
      //Normalise arguments
      if (arguments.length == 2) { //selector, view
        view = options;
        options = {};
      } else if (arguments.length == 1) { //view
        view = selector;
        options = {};
        selector = null;
      }

      //Generate an ID if not given
      var id = options.id || _.uniqueId();

      //Add to the list of children
      var child = this.children[id] = {
        selector: selector,
        view: view
      };

      //If this view is already rendered, add the child automatically
      if (this.rendered) {
        this.renderView(id);
      }

      return view;
    },

    /**
     * Get a child view by ID
     *
     * @param {Mixed} id      The child view ID
     *
     * @return {View|Null}
     */
    getView: function(id) {
      var child = this.children[id];

      return child ? child.view : null;
    },

    /**
     * Renders the main element with the template.
     *
     * If using a template and the 'unwrap' property is true then the template HTML will not be wrapped
     * in an extra tag. This can make editing and styling templates easier.
     *
     * @return {View}     Returns self
     */
    render: function() {
      if (this.template) {
        var html = '';

        //Get data for template
        var data = _.isFunction(this.templateData) ? this.templateData() : this.templateData;

        //Add template helpers
        _.extend(data, this.renderHelpers);

        //Render HTML
        html = Backbone.$(this.template(data));

        //If using unwrap and this is an update, only replace the main element contents
        //This ensures the view that's actually in the DOM is updated
        if (this.unwrap) {
          if (!this.rendered) {
            this.setElement(html);
          } else {
            this.$el.html(html.html());
            this.delegateEvents();
          }
        } else {
          this.$el.html(html);
        }
      }

      this.renderViews();

      if (this.onRender) this.onRender();

      this.rendered = true;

      return this;
    },

    /**
     * Render all child views
     */
    renderViews: function() {
      var self = this,
          ids = _.keys(this.children);

      _.each(ids, _.bind(self.renderView, this));
    },

    /**
     * Renders a child view and adds it to it's locations in the parent element
     *
     * @param {Mixed} id    The child view ID
     */
    renderView: function(id) {
      var child = this.children[id];

      var selector = child.selector,
          view = child.view,
          $el = null;

      view.render();

      //Decide where the child view is going
      if (selector) {
        $el = this.$el.find(selector);
      } else {
        $el = this.$el;
      }

      $el.append(view.el);
    },

    /**
     * Close and remove all child views
     */
    removeViews: function() {
      var self = this,
          ids = _.keys(this.children);

      _.each(ids, _.bind(self.removeView, this));
    },

    /**
     * Close and remove a child view
     *
     * @param {Mixed} id    Child view ID
     */
    removeView: function(id) {
      var children = this.children,
          child = children[id];

      if (!child) return;

      child.view.remove();

      delete children[id];
    },

    /**
     * Closes child views then this one, removes event listeners etc.
     */
    remove: function() {
      this.removeViews();

      Backbone.View.prototype.remove.call(this);
    }
  });

  /**
   * CollectionView
   * Renders a list of ItemViews representing a collection
   */
  var CollectionView = XView.extend({
    tagName: 'ul',

    //Selector string for where items will be inserted. If not set, will default to the root element
    listSelector: null,

    //Selector string for content to show if there are no items
    fallbackSelector: null,

    //Selector string for content to show if there are no items
    loadingSelector: null,

    /**
     * Constructor
     *
     * @param {Object} options
     * @param {Collection} options.collection   Collection to render
     * @param {XView} options.itemView          Constructor (not instance) for an item view
     */
    constructor: function(options) {
      XView.prototype.constructor.call(this, options);

      options = options || {};

      if (options.collection) this.collection = options.collection;
      if (!this.collection) throw new Error('collection is required');

      if (options.itemView) this.itemView = options.itemView;
      if (!this.itemView) throw new Error('itemView is required');

      this.listenTo(this.collection, 'add', this.addItem);
      this.listenTo(this.collection, 'remove', this.removeItem);
      this.listenTo(this.collection, 'reset', this.resetItems);
      this.listenTo(this.collection, 'request', this.onRequest);
      this.listenTo(this.collection, 'sync', this.onSync);

      //Render items if already in the collection
      if (this.collection.length) this.resetItems();
    },

    render: function() {
      XView.prototype.render.call(this);

      //Show loading if collection is being fetched
      if (this.isLoading) {
        this.showLoading();

        this.hideFallback();
      }

      //Not loading; show fallback if collection is empty
      else {
        this.hideLoading();

        if (this.collection.length) {
          this.hideFallback();
        } else {
          this.showFallback();
        }
      }

      return this;
    },

    /**
     * Shows the fallback element using this.fallbackSelector
     */
    showFallback: function() {
      this.$(this.fallbackSelector).show();
    },

    /**
     * Hides the fallback element using this.fallbackSelector
     */
    hideFallback: function() {
      this.$(this.fallbackSelector).hide();
    },

    /**
     * Shows the loading element using this.loadingSelector
     */
    showLoading: function() {
      this.$(this.loadingSelector).show();
    },

    /**
     * Hides the loading element using this.loadingSelector
     */
    hideLoading: function() {
      this.$(this.loadingSelector).hide();
    },

    /**
     * Renders a new model
     *
     * @param {Model} model
     */
    addItem: function(model) {
      var view = new this.itemView({
        model: model
      });

      this.addView(this.listSelector || null, { id: model.cid }, view);
    },

    /**
     * @param {Model} model
     */
    removeItem: function(model) {
      this.removeView(model.cid);
    },

    /**
     * Removes existing item views and adds the current collection contents
     */
    resetItems: function() {
      this.removeViews();

      this.collection.each(_.bind(this.addItem, this));

      //Show fallback if there are no items
      if (!this.collection.length) {
        this.showFallback();
      }
    },

    /**
     * Callback for when a request has started, i.e. collection is in loading state
     */
    onRequest: function() {
      this.isLoading = true;

      //Show loading if there are no items
      if (!this.collection.length) {
        this.showLoading();
      }
    },

    /**
     * Callback for when a request has ended, i.e. collection is not in loading state
     */
    onSync: function() {
      //Hide loading
      this.isLoading = false;
      this.hideLoading();

      //Show fallback if there are no items
      if (!this.collection.length) {
        this.showFallback();
      }
    }
  });

  /**
   * Exports
   */
  XView.Collection = CollectionView;

  Backbone.XView = XView;

  //For use in NodeJS
  if (typeof module != 'undefined') module.exports = XView;

  return Backbone.XView;

}));
<|endoftext|>


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

var request = require("request");
var cheerio = require("cheerio");
var colors = require("colors");
var path = require("path");
var fs = require("fs");
var childProcess = require('child_process');

var fetch = function (text, callback) {
    var encoded = encodeURIComponent(text)
    var options = {
        "url": "http://tw.dictionary.search.yahoo.com/search?p="+encoded,
        "headers": {
            // Suggestion only shown if user-agent set.
            'User-Agent': "Mozilla/5.0",
        },
    };
    request(options, callback);
};

var parseKeywords = function ($, cur) {
    return cur.find("b b").map(function (i, elem) {
        return $(this).text();
    }).get();
};

var parseExamples = function ($, cur) {
    return cur.find(".example_translation")
    .filter(function (i, elem) {
        return $(this).text().length > 0;
    })
    .map(function (i, elem) {
        var parent = $(this).parent();
        $(this).remove();
        return {
            "english": parent.text(),
            "chinese": $(this).text(),
            "keywords": parseKeywords($, parent),
        }
    }).get();
};

var parseExplanations = function ($, cur) {
    return cur.find("li").map(function (i, elem) {
        return {
            "explanation": $(this).find("h4").text().replace(/\d+\. /, ""),
            "examples": parseExamples($, $(this)),
        };
    }).get();
};

var parseTypes = function ($, cur) {
    return cur.find(".lst").first().find(".compArticleList").map(function (i, elem) {
        return {
            "desc": $(this).prev().find(".title").text(),
            "explanations": parseExplanations($, $(this)),
        };
    }).get();
};

var parseAudio = function ($, cur) {
    try {
        var obj = JSON.parse(cur.find("#iconStyle").text());
        if (obj["sound_url_1"].length === 0) return null;
        return obj["sound_url_1"].filter(function (elem) {
            return ('mp3' in elem);
        })[0]['mp3'];
    } catch(e) {
        return null;
    }
};

var parse = function (data) {
    var $ = cheerio.load(data);
    var root = $("#results");
    return {
        "word": root.find("#term").first().text(),
        "pronunciation": root.find("#pronunciation_pos").text(),
        "audio": parseAudio($, root),
        "types": parseTypes($, root),
        "suggesstion": root.find(".VertQuerySuggestion").find("a").first().text(),
    };
};

var highlighter = function (pre, cur) {
    var re = new RegExp('\\b'+cur+'\\b')
    return pre.replace(re, cur.bold);
};

var display = function (text, info) {
    if (text !== info.word) {
        console.log(info.word.cyan.bold.underline);
    }
    if (info.pronunciation) {
        console.log(info.pronunciation.replace(/\[([^\]]+)\]/g, ": ["+"$1".bold+"]"));
    }
    info.types.forEach(function (type) {
        console.log("%s", type.desc.red.bold);
        for (var i=0; i<type.explanations.length; i++) {
            var exp = type.explanations[i];
            console.log("  %d. %s", i+1, exp.explanation);
            for (var j=0; j<exp.examples.length; j++) {
                var ex = exp.examples[j];
                console.log("     %s",
                        ex.keywords.reduce(highlighter, ex.english).cyan);
                console.log("     %s", ex.chinese.green);
            }
        }
    });
    return 0;
};

var play = function (config, info) {
    if (config.playerCmd) {
        childProcess.exec(config.playerCmd + " " + info.audio);
    }
};

var loadConfig = function () {
    var name = (process.platform=="win32") ? "USERPROFILE" : "HOME";
    var file = path.join(process.env[name], ".ydict.json");
    if (!fs.existsSync(file)) return {};
    return require(file);
}

exports.lookup = function (text, callback) {
    fetch(text, function (err, response, data) {
        if (err) {
            callback(err);
        } else if (response.statusCode != 200) {
            callback("Status " + response.statusCode);
        } else {
            callback(null, parse(data));
        }
    });
};

var main = function () {
    var args = process.argv.splice(2);
    if (args.length === 0) {
        console.error("Error: Word or phrase needed.");
        process.exit(1);
    }
    var config = loadConfig();
    var text = args.join(" ");
    exports.lookup(text, function callback (err, info) {
        if (err) {
            console.error("Error:", err);
            process.exit(2);
        } else {
            if (info.word) {
                play(config, info);
                display(text, info);
            } else if (info.suggesstion) {
                console.warn("拼字檢查: %s -> %s".red.bold,
                        text, info.suggesstion);
                exports.lookup(info.suggesstion, callback);
            } else {
                console.log("查無此字".red.bold);
                process.exit(3);
            }
        }
    });
};

if (require.main === module) {
    main();
}
<commit_msg>Fix parsing of audio<commit_after>#!/usr/bin/env node
"use strict";

var request = require("request");
var cheerio = require("cheerio");
var colors = require("colors");
var path = require("path");
var fs = require("fs");
var childProcess = require('child_process');

var fetch = function (text, callback) {
    var encoded = encodeURIComponent(text)
    var options = {
        "url": "http://tw.dictionary.search.yahoo.com/search?p="+encoded,
        "headers": {
            // Suggestion only shown if user-agent set.
            'User-Agent': "Mozilla/5.0",
        },
    };
    request(options, callback);
};

var parseKeywords = function ($, cur) {
    return cur.find("b b").map(function (i, elem) {
        return $(this).text();
    }).get();
};

var parseExamples = function ($, cur) {
    return cur.find(".example_translation")
    .filter(function (i, elem) {
        return $(this).text().length > 0;
    })
    .map(function (i, elem) {
        var parent = $(this).parent();
        $(this).remove();
        return {
            "english": parent.text(),
            "chinese": $(this).text(),
            "keywords": parseKeywords($, parent),
        }
    }).get();
};

var parseExplanations = function ($, cur) {
    return cur.find("li").map(function (i, elem) {
        return {
            "explanation": $(this).find("h4").text().replace(/\d+\. /, ""),
            "examples": parseExamples($, $(this)),
        };
    }).get();
};

var parseTypes = function ($, cur) {
    return cur.find(".lst").first().find(".compArticleList").map(function (i, elem) {
        return {
            "desc": $(this).prev().find(".title").text(),
            "explanations": parseExplanations($, $(this)),
        };
    }).get();
};

var parseAudio = function ($, cur) {
    try {
        var obj = JSON.parse(cur.find("#iconStyle").first().text());
        if (obj["sound_url_1"].length === 0) return null;
        return obj["sound_url_1"].filter(function (elem) {
            return ('mp3' in elem);
        })[0]['mp3'];
    } catch(e) {
        return null;
    }
};

var parse = function (data) {
    var $ = cheerio.load(data);
    var root = $("#results");
    return {
        "word": root.find("#term").first().text(),
        "pronunciation": root.find("#pronunciation_pos").text(),
        "audio": parseAudio($, root),
        "types": parseTypes($, root),
        "suggesstion": root.find(".VertQuerySuggestion").find("a").first().text(),
    };
};

var highlighter = function (pre, cur) {
    var re = new RegExp('\\b'+cur+'\\b')
    return pre.replace(re, cur.bold);
};

var display = function (text, info) {
    if (text !== info.word) {
        console.log(info.word.cyan.bold.underline);
    }
    if (info.pronunciation) {
        console.log(info.pronunciation.replace(/\[([^\]]+)\]/g, ": ["+"$1".bold+"]"));
    }
    info.types.forEach(function (type) {
        console.log("%s", type.desc.red.bold);
        for (var i=0; i<type.explanations.length; i++) {
            var exp = type.explanations[i];
            console.log("  %d. %s", i+1, exp.explanation);
            for (var j=0; j<exp.examples.length; j++) {
                var ex = exp.examples[j];
                console.log("     %s",
                        ex.keywords.reduce(highlighter, ex.english).cyan);
                console.log("     %s", ex.chinese.green);
            }
        }
    });
    return 0;
};

var play = function (config, info) {
    if (config.playerCmd) {
        childProcess.exec(config.playerCmd + " " + info.audio);
    }
};

var loadConfig = function () {
    var name = (process.platform=="win32") ? "USERPROFILE" : "HOME";
    var file = path.join(process.env[name], ".ydict.json");
    if (!fs.existsSync(file)) return {};
    return require(file);
}

exports.lookup = function (text, callback) {
    fetch(text, function (err, response, data) {
        if (err) {
            callback(err);
        } else if (response.statusCode != 200) {
            callback("Status " + response.statusCode);
        } else {
            callback(null, parse(data));
        }
    });
};

var main = function () {
    var args = process.argv.splice(2);
    if (args.length === 0) {
        console.error("Error: Word or phrase needed.");
        process.exit(1);
    }
    var config = loadConfig();
    var text = args.join(" ");
    exports.lookup(text, function callback (err, info) {
        if (err) {
            console.error("Error:", err);
            process.exit(2);
        } else {
            if (info.word) {
                play(config, info);
                display(text, info);
            } else if (info.suggesstion) {
                console.warn("拼字檢查: %s -> %s".red.bold,
                        text, info.suggesstion);
                exports.lookup(info.suggesstion, callback);
            } else {
                console.log("查無此字".red.bold);
                process.exit(3);
            }
        }
    });
};

if (require.main === module) {
    main();
}
<|endoftext|>


<commit_before>'use strict';

/** Dependencies */
var xml2js = require('xml2js');
var parseString = xml2js.parseString;

/**
 * Parse XRoad SOAP to JS
 *
 * Provides defaults if options argument is not specified.
 *
 * @param {String} xml
 * @param {Object} a
 * @param {Function} b
 * @return {Object}
 */
module.exports = function (xml, a, b) {
  var options, callback;

  if (xml !== null || xml !== undefined) {
    if (typeof a === 'function' && (b === null || b === undefined)) {
      callback = a;

      /** Default options for parseString */
      options = {
        explicitArray: false
      };

    } else if (typeof a === 'object' && typeof b === 'function') {
      callback = b;
      options = a;
    } else {
      throw new Error('Invalid arguments for fromXR function.');
    }
  } else {
    throw new Error('No XML argument for parsing.');
  }

  return parseString(xml, options, callback);
};
<commit_msg>Wrap in initial function for parsing before returning<commit_after>'use strict';

/** Dependencies */
var xml2js = require('xml2js');
var parseString = xml2js.parseString;

/**
 * Parse XRoad SOAP to JS
 *
 * Provides defaults if options argument is not specified.
 *
 * @param {String} xml
 * @param {Object} a
 * @param {Function} b
 * @return {Object}
 */
module.exports = function (xml, a, b) {
  var options, callback;

  if (xml !== null || xml !== undefined) {
    if (typeof a === 'function' && (b === null || b === undefined)) {
      callback = a;

      /** Default options for parseString */
      options = {
        explicitArray: false
      };

    } else if (typeof a === 'object' && typeof b === 'function') {
      callback = b;
      options = a;
    } else {
      throw new Error('Invalid arguments for fromXR function.');
    }
  } else {
    throw new Error('No XML argument for parsing.');
  }

  return parseString(xml, options, function (err, res) {
    callback(err, res);
  });
};
<|endoftext|>