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

const Q = require('q');
const _ = require('lodash');

const db = require('./util/dbHelper');
const mails = require('./util/mails');
const tshirts = require('./tshirts');
const startNumbers = require('./startNumbers');
const startBlocks = require('./startblocks');
const editUrlHelper = require('../domain/editUrlHelper');
const timeCalculator = require('../domain/timeCalculator');
const race = require('./race');
const queryHelper = require('./util/queryHelper');

let participants = {
  get: {}
};

participants.get.allWithPaymentStatus = (paymentStatus) => {
  if (_.isUndefined(paymentStatus)) {
    return db.select('select * from participants where firstname != \'\' order by firstname,lastname');
  } else {
    return db.select('select * from participants where has_payed = $1 and firstname != \'\' order by firstname,lastname', [paymentStatus]);
  }
};

participants.get.registered = () => {
  return participants.get.allWithPaymentStatus(false);
};

participants.get.confirmed = () => {
  return participants.get.allWithPaymentStatus(true);
};

participants.get.blancParticipants = () => {
  return db.select('select * from participants where is_on_site_registration = true');
};

participants.get.all = () => {
  return db.select('select * from participants');
};

participants.get.forDataTables = (start, length, search, ordering) => {
  const queries = queryHelper.dataTablesQueries({
    count: 'ID',
    table: 'PARTICIPANTS',
    baseFilter: `visibility = 'yes' and has_payed = true and firstname != ''`,
    select: 'ID, START_NUMBER, FIRSTNAME, LASTNAME, TEAM',
    filterColumns: ['FIRSTNAME', 'LASTNAME', 'TEAM'],
    searchParamName: '$1',
    paging: {offset: start, length: length},
    ordering,
  });

  return db.selectForDataTables(queries, search);
};

participants.get.byId = (id) => {
  return db.select('SELECT * FROM participants WHERE id = $1', [id])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.get.byStartnumber = (number) => {
  return db.select('SELECT * FROM participants WHERE start_number = $1', [number])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.get.bySecureId = (id) => {
  return db.select('SELECT * FROM participants WHERE secureid = $1', [id])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.save = (participant) => {
  return db.insert(`INSERT INTO participants
                    (firstname, lastname, email, category, birthyear, team, visibility,discount, paymenttoken, secureid, start_number, start_block, couponcode, goal, registration_time)
                    values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) returning id`,
    [participant.firstname,
      participant.lastname,
      participant.email,
      participant.category,
      participant.birthyear,
      participant.team,
      participant.visibility,
      participant.discount,
      participant.paymentToken,
      participant.secureID,
      participant.start_number,
      '1',
      participant.couponcode,
      participant.goal,
      participant.registrationTime
    ]
  );
};

participants.saveBlancParticipants = (amount) => {

  return startNumbers.next().then( nr => {
    let startNumberList =  _.range(nr, nr + amount);
    return Q.all(startNumberList.map(participants.saveBlanc));
  });
};

participants.choseStartBlock = (startNumber) => { //move this out of here TODO
  return null;
};

participants.saveBlanc = (startnumber) => {
  let participant = {
    firstname: '',
    lastname: '',
    email: '',
    category: '',
    birthyear: 0,
    team: '',
    visibility: 'yes',
    discount: 'no',
    paymentToken: 'on-site Registrierung (' + startnumber + ')',
    secureID: editUrlHelper.generateSecureID(),
    start_number: startnumber,
    start_block: participants.choseStartBlock(startnumber),
    is_on_site_registration: true,
    has_payed: false,
    goal: 'relaxed'
  };

  return db.insert(`INSERT INTO participants
                    (firstname, lastname, email, category, birthyear, team, visibility,discount, paymenttoken, secureid, start_number, start_block, is_on_site_registration, has_payed, goal)
                    values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) returning id`,
    [participant.firstname,
      participant.lastname,
      participant.email,
      participant.category,
      participant.birthyear,
      participant.team,
      participant.visibility,
      participant.discount,
      participant.paymentToken,
      participant.secureID,
      participant.start_number,
      participant.start_block,
      participant.is_on_site_registration,
      participant.has_payed,
      participant.goal
    ]
  );
};

participants.delete = (participantid) => {
  return db.delete('delete from participants where id=$1', [participantid]);
};

participants.update = (participant, id) => {
    return db.update(`UPDATE participants SET
                    (firstname, lastname, email, category, birthyear, team, visibility, start_block) =
                    ($1, $2, $3, $4, $5, $6, $7, $8)
                    WHERE secureid = $9`,
    [participant.firstname,
     participant.lastname,
     participant.email,
     participant.category,
     participant.birthyear,
     participant.team,
     participant.visibility,
     participant.start_block,
     id])
    .then(() => {
      participants.get.bySecureId(id).then( saved_participant => {
        if (saved_participant.time > 0) {
          participants.updateTime(saved_participant.start_number,saved_participant.time);
        }
      });
    });
};

participants.markPayed = (participantId) => {
  return db.update('update participants SET has_payed = true, confirmation_time = current_timestamp(2) WHERE id = $1', [participantId])
    .then(result => {
      if (result < 1) {
        throw new Error('Es konnte kein Teilnehmer mit ID: ' + participantId + ' gefunden werden.');
      }
      return participantId;
    });
};

participants.updateTime = (startnumber, finishtime) => {
  return participants.get.byStartnumber(startnumber)
    .then(participant => {
      return startBlocks.times().then((startTimes) => {
        let seconds = timeCalculator.relativeSeconds(startTimes,finishtime,participant.start_block);
        if ((finishtime <= participant.time ) || _.isEmpty(participant.time)) {
          return db.update('update participants set time=$2,seconds=$3 where start_number=$1', [startnumber, finishtime, seconds]);
        }
      });
    });
};

participants.insertTime = (startnumber, timestring) => {
  return participants.updateTime(startnumber, timeCalculator.timestamp(timestring));
};

participants.getTime = (startnumber) => {
  const deferred = Q.defer();
  db.select('select seconds from participants where start_number=$1', [startnumber])
    .then((result) => {
      deferred.resolve(result[0].seconds);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.rank = (startnumber) => {
  const deferred = Q.defer();
  db.select("select pos from (select seconds,start_number,rank() over (order by seconds) as pos from participants where visibility='yes') as ss where start_number=$1;", [startnumber])
    .then((result) => {
      deferred.resolve(result[0].pos);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.rankByCategory = (startnumber) => {
  const deferred = Q.defer();
  db.select("select pos from (select seconds,start_number,rank() over (partition by category order by seconds) as pos from participants where visibility='yes') as ss where start_number=$1;", [startnumber])
    .then((result) => {
      deferred.resolve(result[0].pos);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.bulkmail = () => {
  const deferred = Q.defer();

  participants.get.confirmed().then(confirmed => {
    participants.get.registered().then(unconfirmed => {
      _.forEach(confirmed, participant => {
        sendInfoMailTo(participant);
      });
      _.forEach(unconfirmed, participant => {
        sendInfoMailTo(participant);
      });
      deferred.resolve();
    });
  }).fail(deferred.reject);

  return deferred.promise;
};

function sendInfoMailTo(participant) {
    mails.sendStatusEmail(participant, 'Lauf gegen Rechts - Infos zum Lauf', 'views/participants/bulkmail.pug');
}

module.exports = participants;
<commit_msg>always use index 0 for startblocks until we can<commit_after>/* jshint node: true */
/* jshint esnext: true */
'use strict';

const Q = require('q');
const _ = require('lodash');

const db = require('./util/dbHelper');
const mails = require('./util/mails');
const tshirts = require('./tshirts');
const startNumbers = require('./startNumbers');
const startBlocks = require('./startblocks');
const editUrlHelper = require('../domain/editUrlHelper');
const timeCalculator = require('../domain/timeCalculator');
const race = require('./race');
const queryHelper = require('./util/queryHelper');

let participants = {
  get: {}
};

participants.get.allWithPaymentStatus = (paymentStatus) => {
  if (_.isUndefined(paymentStatus)) {
    return db.select('select * from participants where firstname != \'\' order by firstname,lastname');
  } else {
    return db.select('select * from participants where has_payed = $1 and firstname != \'\' order by firstname,lastname', [paymentStatus]);
  }
};

participants.get.registered = () => {
  return participants.get.allWithPaymentStatus(false);
};

participants.get.confirmed = () => {
  return participants.get.allWithPaymentStatus(true);
};

participants.get.blancParticipants = () => {
  return db.select('select * from participants where is_on_site_registration = true');
};

participants.get.all = () => {
  return db.select('select * from participants');
};

participants.get.forDataTables = (start, length, search, ordering) => {
  const queries = queryHelper.dataTablesQueries({
    count: 'ID',
    table: 'PARTICIPANTS',
    baseFilter: `visibility = 'yes' and has_payed = true and firstname != ''`,
    select: 'ID, START_NUMBER, FIRSTNAME, LASTNAME, TEAM',
    filterColumns: ['FIRSTNAME', 'LASTNAME', 'TEAM'],
    searchParamName: '$1',
    paging: {offset: start, length: length},
    ordering,
  });

  return db.selectForDataTables(queries, search);
};

participants.get.byId = (id) => {
  return db.select('SELECT * FROM participants WHERE id = $1', [id])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.get.byStartnumber = (number) => {
  return db.select('SELECT * FROM participants WHERE start_number = $1', [number])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.get.bySecureId = (id) => {
  return db.select('SELECT * FROM participants WHERE secureid = $1', [id])
    .then(result => {
      if (_.isEmpty(result)) {
        throw new Error('No participant found');
      }
      return result;
    })
    .then(result => result[0]);
};

participants.save = (participant) => {
  return db.insert(`INSERT INTO participants
                    (firstname, lastname, email, category, birthyear, team, visibility,discount, paymenttoken, secureid, start_number, start_block, couponcode, goal, registration_time)
                    values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) returning id`,
    [participant.firstname,
      participant.lastname,
      participant.email,
      participant.category,
      participant.birthyear,
      participant.team,
      participant.visibility,
      participant.discount,
      participant.paymentToken,
      participant.secureID,
      participant.start_number,
      '0',
      participant.couponcode,
      participant.goal,
      participant.registrationTime
    ]
  );
};

participants.saveBlancParticipants = (amount) => {

  return startNumbers.next().then( nr => {
    let startNumberList =  _.range(nr, nr + amount);
    return Q.all(startNumberList.map(participants.saveBlanc));
  });
};

participants.choseStartBlock = (startNumber) => { //move this out of here TODO
  return null;
};

participants.saveBlanc = (startnumber) => {
  let participant = {
    firstname: '',
    lastname: '',
    email: '',
    category: '',
    birthyear: 0,
    team: '',
    visibility: 'yes',
    discount: 'no',
    paymentToken: 'on-site Registrierung (' + startnumber + ')',
    secureID: editUrlHelper.generateSecureID(),
    start_number: startnumber,
    start_block: participants.choseStartBlock(startnumber),
    is_on_site_registration: true,
    has_payed: false,
    goal: 'relaxed'
  };

  return db.insert(`INSERT INTO participants
                    (firstname, lastname, email, category, birthyear, team, visibility,discount, paymenttoken, secureid, start_number, start_block, is_on_site_registration, has_payed, goal)
                    values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) returning id`,
    [participant.firstname,
      participant.lastname,
      participant.email,
      participant.category,
      participant.birthyear,
      participant.team,
      participant.visibility,
      participant.discount,
      participant.paymentToken,
      participant.secureID,
      participant.start_number,
      participant.start_block,
      participant.is_on_site_registration,
      participant.has_payed,
      participant.goal
    ]
  );
};

participants.delete = (participantid) => {
  return db.delete('delete from participants where id=$1', [participantid]);
};

participants.update = (participant, id) => {
    return db.update(`UPDATE participants SET
                    (firstname, lastname, email, category, birthyear, team, visibility, start_block) =
                    ($1, $2, $3, $4, $5, $6, $7, $8)
                    WHERE secureid = $9`,
    [participant.firstname,
     participant.lastname,
     participant.email,
     participant.category,
     participant.birthyear,
     participant.team,
     participant.visibility,
     participant.start_block,
     id])
    .then(() => {
      participants.get.bySecureId(id).then( saved_participant => {
        if (saved_participant.time > 0) {
          participants.updateTime(saved_participant.start_number,saved_participant.time);
        }
      });
    });
};

participants.markPayed = (participantId) => {
  return db.update('update participants SET has_payed = true, confirmation_time = current_timestamp(2) WHERE id = $1', [participantId])
    .then(result => {
      if (result < 1) {
        throw new Error('Es konnte kein Teilnehmer mit ID: ' + participantId + ' gefunden werden.');
      }
      return participantId;
    });
};

participants.updateTime = (startnumber, finishtime) => {
  return participants.get.byStartnumber(startnumber)
    .then(participant => {
      return startBlocks.times().then((startTimes) => {
        let seconds = timeCalculator.relativeSeconds(startTimes,finishtime,participant.start_block);
        if ((finishtime <= participant.time ) || _.isEmpty(participant.time)) {
          return db.update('update participants set time=$2,seconds=$3 where start_number=$1', [startnumber, finishtime, seconds]);
        }
      });
    });
};

participants.insertTime = (startnumber, timestring) => {
  return participants.updateTime(startnumber, timeCalculator.timestamp(timestring));
};

participants.getTime = (startnumber) => {
  const deferred = Q.defer();
  db.select('select seconds from participants where start_number=$1', [startnumber])
    .then((result) => {
      deferred.resolve(result[0].seconds);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.rank = (startnumber) => {
  const deferred = Q.defer();
  db.select("select pos from (select seconds,start_number,rank() over (order by seconds) as pos from participants where visibility='yes') as ss where start_number=$1;", [startnumber])
    .then((result) => {
      deferred.resolve(result[0].pos);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.rankByCategory = (startnumber) => {
  const deferred = Q.defer();
  db.select("select pos from (select seconds,start_number,rank() over (partition by category order by seconds) as pos from participants where visibility='yes') as ss where start_number=$1;", [startnumber])
    .then((result) => {
      deferred.resolve(result[0].pos);
    })
    .catch(deferred.reject);
  return deferred.promise;
};

participants.bulkmail = () => {
  const deferred = Q.defer();

  participants.get.confirmed().then(confirmed => {
    participants.get.registered().then(unconfirmed => {
      _.forEach(confirmed, participant => {
        sendInfoMailTo(participant);
      });
      _.forEach(unconfirmed, participant => {
        sendInfoMailTo(participant);
      });
      deferred.resolve();
    });
  }).fail(deferred.reject);

  return deferred.promise;
};

function sendInfoMailTo(participant) {
    mails.sendStatusEmail(participant, 'Lauf gegen Rechts - Infos zum Lauf', 'views/participants/bulkmail.pug');
}

module.exports = participants;
<|endoftext|>


<commit_before>var config = require('./config.js').getConfig(process.env.NODE_ENV);

if (config.test_mode === false) {
  var redis = require('redis')
  var rdb = redis.createClient(config.redis.port, config.redis.host);
  console.log("Redis Connected " + config.redis.host + ":" + config.redis.port);
  if (config.redis.auth) {
    rdb.auth(config.redis.auth);
    console.log("Redis Authenticated " + config.redis.auth);
  }
} else {
  console.log("Running in TEST MODE")
  var redis = require('mock-redis-client').createMockRedis();
  var rdb = redis.createClient();
}

/**
 * data Functions
 */

exports.getUser = function(email, cb) {
  key = 'user:' + email;
  rdb.hgetall(key, function(err, user) {
    cb(null, user);
  });
};

exports.addUser = function(email, data, cb) {
  key = 'user:' + email;
  user = {
    firstName: data.firstName,
    lastName: data.lastName
  }
  key = 'user:' + email
  rdb.hgetall(key, function(err, user) {
    if (err) cb(err, null)
    if (user) cb('User Exists', null)
    rdb.hmset(key, user, function(err, reply) {
      if (err) cb(err, null)
      cb(null, user);
    });
  });
};

exports.addTransfer = function(email, points, cb) {
  key = 'transfers:' + email;
  skey = 'points:' + email;
  rdb.rpush(key, points, function (err, response) {
    if (err) cb(err, null)
    exports.getPoints(email, function(err, userPoints) {
      if (err) throw new Error(err);
      cb(null, userPoints);
    });
  });
};

exports.getTransfers = function(email, cb) {
  key = 'transfers:' + email;
  skey = 'points:' + email;
  rdb.lrange(key, 0, -1, function (err, items) {
    if (err) cb(err, null)
    var sum = 0
    if (items.length > 0) {
      items.forEach(function (item) {
        sum = sum + parseInt(item);
      });
    }
    rdb.set(skey, sum, function (err, response) {
      cb(null, items);
    });
  });
};

exports.getPoints = function(email, cb) {
  exports.getTransfers(email, function (err, items) {
    if (err) cb(err, null)
    rdb.get(skey, function (err, value) {
      cb(err, parseInt(value));
    });
  });
};

/**
 * Response Functions
 */
exports.returnResults = function(res, data) {
  console.log("Returning Results");
  ret_data = {
    "errors" : [],
    "ok" : true,
    "results" : data,
    "status" : 0
  };
  res.status(200).send(ret_data)
  res.end();
};

exports.param_error = function(res, error) {
  console.log("Returning Error");
  if (res.headersSent === false) {
    var ret_data = {
      "errors" : [
        error
      ],
      "ok" : false,
      "status" : 400
    };
    res.status(400).send(ret_data)
    res.end();
  } else {
    res.end();
  }
};

exports.param_missing = function(res, errors) {
  console.log("Returning Error");
  if (res.headersSent === false) {
    var ret_data = {
      "errors" : errors,
      "ok" : false,
      "status" : 400
    };
    res.status(400).send(ret_data)
    res.end();
  } else {
    res.end();
  }
};

<commit_msg>fixx add user<commit_after>var config = require('./config.js').getConfig(process.env.NODE_ENV);

if (config.test_mode === false) {
  var redis = require('redis')
  var rdb = redis.createClient(config.redis.port, config.redis.host);
  console.log("Redis Connected " + config.redis.host + ":" + config.redis.port);
  if (config.redis.auth) {
    rdb.auth(config.redis.auth);
    console.log("Redis Authenticated " + config.redis.auth);
  }
} else {
  console.log("Running in TEST MODE")
  var redis = require('mock-redis-client').createMockRedis();
  var rdb = redis.createClient();
}

/**
 * data Functions
 */

exports.getUser = function(email, cb) {
  key = 'user:' + email;
  rdb.hgetall(key, function(err, user) {
    cb(null, user);
  });
};

exports.addUser = function(email, user, cb) {
  key = 'user:' + email;
  rdb.hgetall(key, function(err, userFound) {
    if (err) cb(err, null)
    if (userFound) cb('User Exists', null)
    rdb.hmset(key, user, function(err, reply) {
      if (err) cb(err, null)
      cb(null, user);
    });
  });
};

exports.addTransfer = function(email, points, cb) {
  key = 'transfers:' + email;
  skey = 'points:' + email;
  rdb.rpush(key, points, function (err, response) {
    if (err) cb(err, null)
    exports.getPoints(email, function(err, userPoints) {
      if (err) throw new Error(err);
      cb(null, userPoints);
    });
  });
};

exports.getTransfers = function(email, cb) {
  key = 'transfers:' + email;
  skey = 'points:' + email;
  rdb.lrange(key, 0, -1, function (err, items) {
    if (err) cb(err, null)
    var sum = 0
    if (items.length > 0) {
      items.forEach(function (item) {
        sum = sum + parseInt(item);
      });
    }
    rdb.set(skey, sum, function (err, response) {
      cb(null, items);
    });
  });
};

exports.getPoints = function(email, cb) {
  exports.getTransfers(email, function (err, items) {
    if (err) cb(err, null)
    rdb.get(skey, function (err, value) {
      cb(err, parseInt(value));
    });
  });
};

/**
 * Response Functions
 */
exports.returnResults = function(res, data) {
  console.log("Returning Results");
  ret_data = {
    "errors" : [],
    "ok" : true,
    "results" : data,
    "status" : 0
  };
  res.status(200).send(ret_data)
  res.end();
};

exports.param_error = function(res, error) {
  console.log("Returning Error");
  if (res.headersSent === false) {
    var ret_data = {
      "errors" : [
        error
      ],
      "ok" : false,
      "status" : 400
    };
    res.status(400).send(ret_data)
    res.end();
  } else {
    res.end();
  }
};

exports.param_missing = function(res, errors) {
  console.log("Returning Error");
  if (res.headersSent === false) {
    var ret_data = {
      "errors" : errors,
      "ok" : false,
      "status" : 400
    };
    res.status(400).send(ret_data)
    res.end();
  } else {
    res.end();
  }
};

<|endoftext|>


<commit_before>var parseUrl = require('url').parse;
var http = require('http');
var request = require('request');
var querystring = require('querystring');
var redis = require("redis");

var env = process.env;
var chost = process.env.CAS_HOST;
var _ = require('underscore');

/**
 * CAS validate:  validate requests via CAS service tickets
 *
 * Options:
 *
 *   - `serivce`  the service url we are checking.  probably best to leave it blank
 *   - `cas_host` the CAS host.  will fallback to environment variable CAS_HOST
 *
 * @param {Object} options
 * @return {Function}
 * @api public
 */

var redclient = redis.createClient();

redclient.on("error", function (err) {
    console.log("Error " + err);
});

function username(req,res,next){
    res.setHeader('Content-Type','application/json');
    if(req.session !== undefined && req.session.name !== undefined){
        return res.end(JSON.stringify({'user':req.session.name}));
    }else{
        return res.end(JSON.stringify({'user':null}));
    }
}

// these should be settable options
var validation_service = '/cas/serviceValidate';
var login_service = '/cas/login';

function session_or_abort(){
    return function(req,res,next){
        console.log('processing session or abort');
        if(req.session !== undefined  && req.session.st){
            console.log('have session.  steady as she goes');
            // okay, pass control
            return next();
        }else{
            console.log('no session, switch to next route');
            return next('route');//new Error('unauthorized'));
        }
    }
}

function check_or_redirect(options){
    // if no session, and no ticket in request, then redirect;
    // service param is optional, will default to whatever URL is used in the request

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    var gateway = options.gateway; // default to false

    return function(req,res,next){
        if(req.session !== undefined  && req.session.st){
            // okay, pass control
            return next();
        }
        // still here? redirect to CAS server
        var url = parseUrl(req.url,true);
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;
        var queryopts = {'service':service};
        if(gateway){
            queryopts.gateway = gateway;
        }
        console.log('no current session, redirecting to CAS server') ;
        // previous version had an if here, with a 403 if request was
        // a json, but that never worked anyway

        res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                              +'?'
                              +querystring.stringify(queryopts)
                           });
        return res.end();
    }
}

function check_no_redirect(options){
    _.extend(options, {'gateway':true});
    return redirect(options);
}

function redirect(options){
    // if no session, and no ticket in request, then redirect;
    // service param is optional, will default to whatever URL is used in the request

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    var gateway = options.gateway; // default to false

    return function(req,res,next){
        //  redirect to CAS server
        var url = parseUrl(req.url,true);
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;
        var queryopts = {'service':service};
        if(gateway){
            queryopts.gateway = gateway;
        }
        console.log('no current session, redirecting to CAS server') ;
        // previous version had an if here, with a 403 if request was
        // a json, but that never worked anyway

        res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                              +'?'
                              +querystring.stringify(queryopts)
                           });
        return res.end();
    }
}

// remainder of the old function
function validate(options){

    // have to validate cas service calls

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var options_service = options.service; //'http://safety.ctmlabs.net/geojson';
    var do_not_redirect = options.do_not_redirect;

    return function validate(req,res,next){
        var url = parseUrl(req.url,true);
        var service = options_service ? options_service :
        'http://'+req.headers.host + url.pathname;
        var method = req.method.toLowerCase();
        // console.log('HITTING VALIDATE: method: '+method);
        if (method == 'post'){
            invalidate(req,res,next);
            return;
        }
//else{
            // no ticket in the request.  check for still valid CAS session
            var st; // temporary service ticket holder
            if(st = req.session.st){
                next();
            }else{
                // not okay.  must log in again
                console.log('ticket not in session store, redirect') ;
                if(/text\/html|\*/.exec(req.headers.accept)){
                    res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                                         +'?'
                                         +querystring.stringify(
                                             {'service':service})});
                    res.end();
                }else if(/json/.exec(req.headers.accept)){
                    res.writeHead(403, { 'Content-Type': 'application/json' });
                    var cas_login_uri =  'https://'+cas_host+login_service
                        +'?'
                        +querystring.stringify(
                            {'service':service});
                    res.end(JSON.stringify({'url':cas_login_uri}));
                }else{
                    res.statusCode=404;
                    res.end();
                }
            }

        }
        return null;
        // https://cas.ctmlabs.net/cas/serviceValidate?service=http://safety.ctmlabs.net/geojson&ticket=ST-2-tpJilV9tKI1aDzsbNIRr-cas
};

var invalidate = exports.invalidate = function invalidate(req,res,next){
    // handling a post here

    // parse out the ticket number from the body, then get the
    // sessionid associated with that ticket, and destroy it.
    for( var param in req.body){
        if(/<samlp:SessionIndex>(.*)<\/samlp:SessionIndex>/.exec(req.body[param])){
            var st = RegExp.$1;
            redclient.get(st,function(err,sid){
                if(!err){
                    req.sessionStore.destroy(sid,function(err){
                        if(err){
                            console.log(err);
                        }
                    });
                    redclient.del(st);
                }
            });
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.end();
            return null;
        }
    }
    next(new Error('Unauthorized'));
    return null;
};

function ssoff(){
    return function(req,res,next){
        var method = req.method.toLowerCase();
        if (method == 'post'){
            return invalidate(req,res,next);
        }else{
            next();
        }
        return null;
    }
}

function ticket(options){

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');
    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';

    return function(req,res,next){
        var url = parseUrl(req.url,true);
        console.log(url);
        if(url.query === undefined || url.query.ticket === undefined){
            console.log('moving along, no ticket');
            return next();
        }
        // have a ticket, try to set up a CAS session
        var url = parseUrl(req.url,true);
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;

        // validate the service ticket
        var ticket = url.query.ticket;
        var cas_uri =  'https://'+cas_host+validation_service
                    +'?'
                    +querystring.stringify(
                        {'service':service,
                         'ticket':ticket});
        return request({uri:cas_uri}, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                if(/cas:authenticationSuccess/.exec(body)){
                    console.log('auth passed ');
                    // stuff the cookie  into a session
                    // and store the ticket as well

                    // valid user
                    if(/<cas:user>(\w+)<\/cas:user>/.exec(body)){
                        req.session.name = RegExp.$1;
                    }
                    req.session.st = ticket;

                    // stuff into a redis session
                    redclient.set(ticket,req.sessionID);
                    next();
                    //res.writeHead(307,{'location':service});
                    //res.end();
                }
            }else{
                console.log('auth failed') ;
                // okay, not logged in, but don't get worked up about it
                next(new Error('authentication failed'));
            }
            return null;

        });
    }
}

exports.redirect = redirect;
exports.check_or_redirect = check_or_redirect;
exports.check_and_return = check_no_redirect;
exports.ticket = ticket;
exports.ssoff = ssoff;
exports.username = username;
exports.session_or_abort = session_or_abort;
<commit_msg>banner working<commit_after>var parseUrl = require('url').parse;
var http = require('http');
var request = require('request');
var querystring = require('querystring');
var redis = require("redis");

var env = process.env;
var chost = process.env.CAS_HOST;
var _ = require('underscore');

/**
 * CAS validate:  validate requests via CAS service tickets
 *
 * Options:
 *
 *   - `serivce`  the service url we are checking.  probably best to leave it blank
 *   - `cas_host` the CAS host.  will fallback to environment variable CAS_HOST
 *
 * @param {Object} options
 * @return {Function}
 * @api public
 */

var redclient = redis.createClient();

redclient.on("error", function (err) {
    console.log("Error " + err);
});

function username(req,res,next){
    res.setHeader('Content-Type','application/json');
    if(req.session !== undefined && req.session.name !== undefined){
        return res.end(JSON.stringify({'user':req.session.name}));
    }else{
        return res.end(JSON.stringify({'user':null}));
    }
}

// these should be settable options
var validation_service = '/cas/serviceValidate';
var login_service = '/cas/login';

function session_or_abort(){
    return function(req,res,next){
        console.log('processing session or abort');
        if(req.session !== undefined  && req.session.st){
            console.log('have session.  steady as she goes');
            // okay, pass control
            return next();
        }else{
            console.log('no session, switch to next route');
            return next('route');//new Error('unauthorized'));
        }
    }
}

function check_or_redirect(options){
    // if no session, and no ticket in request, then redirect;
    // service param is optional, will default to whatever URL is used in the request

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    var gateway = options.gateway; // default to false

    return function(req,res,next){
        if(req.session !== undefined  && req.session.st){
            // okay, pass control
            return next();
        }
        // still here? redirect to CAS server
        var url = parseUrl(req.url,true);
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;
        var queryopts = {'service':service};
        if(gateway){
            queryopts.gateway = gateway;
        }
        console.log('no current session, redirecting to CAS server') ;
        // previous version had an if here, with a 403 if request was
        // a json, but that never worked anyway

        res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                              +'?'
                              +querystring.stringify(queryopts)
                           });
        return res.end();
    }
}

function check_no_redirect(options){
    _.extend(options, {'gateway':true});
    return redirect(options);
}

function redirect(options){
    // if no session, and no ticket in request, then redirect;
    // service param is optional, will default to whatever URL is used in the request

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    var gateway = options.gateway; // default to false

    return function(req,res,next){
        //  redirect to CAS server
        var url = parseUrl(req.url,true);
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;
        var queryopts = {'service':service};
        if(gateway){
            queryopts.gateway = gateway;
        }
        console.log('no current session, redirecting to CAS server') ;
        // previous version had an if here, with a 403 if request was
        // a json, but that never worked anyway

        res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                              +'?'
                              +querystring.stringify(queryopts)
                           });
        return res.end();
    }
}

// remainder of the old function
function validate(options){

    // have to validate cas service calls

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');

    var options_service = options.service; //'http://safety.ctmlabs.net/geojson';
    var do_not_redirect = options.do_not_redirect;

    return function validate(req,res,next){
        var url = parseUrl(req.url,true);
        var service = options_service ? options_service :
        'http://'+req.headers.host + url.pathname;
        var method = req.method.toLowerCase();
        // console.log('HITTING VALIDATE: method: '+method);
        if (method == 'post'){
            invalidate(req,res,next);
            return;
        }
//else{
            // no ticket in the request.  check for still valid CAS session
            var st; // temporary service ticket holder
            if(st = req.session.st){
                next();
            }else{
                // not okay.  must log in again
                console.log('ticket not in session store, redirect') ;
                if(/text\/html|\*/.exec(req.headers.accept)){
                    res.writeHead(307, { 'location': 'https://'+cas_host+login_service
                                         +'?'
                                         +querystring.stringify(
                                             {'service':service})});
                    res.end();
                }else if(/json/.exec(req.headers.accept)){
                    res.writeHead(403, { 'Content-Type': 'application/json' });
                    var cas_login_uri =  'https://'+cas_host+login_service
                        +'?'
                        +querystring.stringify(
                            {'service':service});
                    res.end(JSON.stringify({'url':cas_login_uri}));
                }else{
                    res.statusCode=404;
                    res.end();
                }
            }

        }
        return null;
        // https://cas.ctmlabs.net/cas/serviceValidate?service=http://safety.ctmlabs.net/geojson&ticket=ST-2-tpJilV9tKI1aDzsbNIRr-cas
};

var invalidate = exports.invalidate = function invalidate(req,res,next){
    // handling a post here

    // parse out the ticket number from the body, then get the
    // sessionid associated with that ticket, and destroy it.
    for( var param in req.body){
        if(/<samlp:SessionIndex>(.*)<\/samlp:SessionIndex>/.exec(req.body[param])){
            var st = RegExp.$1;
            redclient.get(st,function(err,sid){
                if(!err){
                    req.sessionStore.destroy(sid,function(err){
                        if(err){
                            console.log(err);
                        }
                    });
                    redclient.del(st);
                }
            });
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.end();
            return null;
        }
    }
    next(new Error('Unauthorized'));
    return null;
};

function ssoff(){
    return function(req,res,next){
        var method = req.method.toLowerCase();
        if (method == 'post'){
            return invalidate(req,res,next);
        }else{
            next();
        }
        return null;
    }
}

function ticket(options){

    var cas_host = options.cas_host ? options.cas_host : chost;
    if (! cas_host ) throw new Error('no CAS host specified');
    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';

    return function(req,res,next){
        var url = parseUrl(req.url,true);

        if(url.query === undefined || url.query.ticket === undefined){
            console.log('moving along, no ticket');
            return next();
        }
        console.log('have ticket')
        // have a ticket, try to set up a CAS session
        var service = opt_service ? opt_service :
            'http://'+req.headers.host + url.pathname;
        console.log('service: '+service)

        // validate the service ticket
        var ticket = url.query.ticket;
        var cas_uri =  'https://'+cas_host+validation_service
                    +'?'
                    +querystring.stringify(
                        {'service':service,
                         'ticket':ticket});
        console.log('firing: '+cas_uri)
        request({uri:cas_uri}, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                if(/cas:authenticationSuccess/.exec(body)){
                    console.log('auth passed ');
                    // stuff the cookie  into a session
                    // and store the ticket as well

                    // valid user
                    if(/<cas:user>(\w+)<\/cas:user>/.exec(body)){
                        req.session.name = RegExp.$1;
                    }
                    req.session.st = ticket;

                    // stuff into a redis session
                    redclient.set(ticket,req.sessionID);
                    next();
                    //res.writeHead(307,{'location':service});
                    //res.end();
                }else{
                    console.log('something else!' + body)
                    next('route')
                }

            }else{
                console.log('auth failed') ;
                // okay, not logged in, but don't get worked up about it
                next(new Error('authentication failed'));
            }
            return null;

        });
    }
}

exports.redirect = redirect;
exports.check_or_redirect = check_or_redirect;
exports.check_and_return = check_no_redirect;
exports.ticket = ticket;
exports.ssoff = ssoff;
exports.username = username;
exports.session_or_abort = session_or_abort;
<|endoftext|>


<commit_before>const debug = require('debug')('chase:user-adaptor');
const AWS = require('aws-sdk');

const s3 = new AWS.S3();

function successResponseBuilder(bodyObj) {
  return {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    body: JSON.stringify(bodyObj),
  };
}

function errorResponseBuilder(error) {
  debug(error);
  const res = error.response || { headers: {} };
  const response = {
    statusCode: res.status,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    body: JSON.stringify({
      message: res.statusText,
    }),
  };
  return response;
}

module.exports.register = (event, context, callback) => {
  const params = JSON.parse(event.body);
  debug('[register]>>>>', params);
  if (!params.accesstoken.match(/^[a-z0-9-]+$/)) {
    errorResponseBuilder('Invalid access token.');
  }
  const obj = {
    Bucket: process.env.BUCKET,
    Key: `accesstokens/${params.accesstoken}.json`,
    Body: JSON.stringify('', null, 2),
  };
  s3.putObject(obj, (err, data) => {
    if (err) {
      callback(null, errorResponseBuilder(err));
    } else {
      callback(null, successResponseBuilder(data));
    }
  });
};

function extractAccesstokens() {
  return new Promise((rv, rj) => {
    const opts = {
      Bucket: process.env.BUCKET,
      Prefix: 'accesstokens/',
      MaxKeys: 10,
    };
    s3.listObjectsV2(opts, (err, data) => {
      if (err) {
        rj(err.stack);
      } else {
        const files = data.Contents.filter(d => d.Key.match(/\.json$/));
        debug(files);
        const tokens = files.map(d => d.Key.match(/^accesstokens\/([a-z0-9-]+)\.json$/)[1]);
        rv(tokens);
      }
    });
  });
}

module.exports.prepare = (event, context, callback) => {
  debug('[prepare]>>>>');
  extractAccesstokens().then((files) => {
    debug(files);
  })
    .then(() => callback(null, successResponseBuilder()))
    .catch(err => callback(null, errorResponseBuilder(err)));
};
<commit_msg>:construction: userprepare<commit_after>require('dotenv').config();
const debug = require('debug')('chase:user-adaptor');
const AWS = require('aws-sdk');

const Pocket = require('./PocketAPI');

const s3 = new AWS.S3();

function successResponseBuilder(bodyObj) {
  return {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    body: JSON.stringify(bodyObj),
  };
}

function errorResponseBuilder(error) {
  debug(error);
  const res = error.response || { headers: {} };
  const response = {
    statusCode: res.status,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    body: JSON.stringify({
      message: res.statusText,
    }),
  };
  return response;
}

module.exports.register = (event, context, callback) => {
  const params = JSON.parse(event.body);
  debug('[register]>>>>', params);
  if (!params.accesstoken.match(/^[a-z0-9-]+$/)) {
    errorResponseBuilder('Invalid access token.');
  }
  const obj = {
    Bucket: process.env.BUCKET,
    Key: `accesstokens/${params.accesstoken}.json`,
    Body: JSON.stringify('', null, 2),
  };
  s3.putObject(obj, (err, data) => {
    if (err) {
      callback(null, errorResponseBuilder(err));
    } else {
      callback(null, successResponseBuilder(data));
    }
  });
};

function extractAccesstokens() {
  return new Promise((rv, rj) => {
    const opts = {
      Bucket: process.env.BUCKET,
      Prefix: 'accesstokens/',
      MaxKeys: 10,
    };
    s3.listObjectsV2(opts, (err, data) => {
      if (err) {
        rj(err.stack);
      } else {
        const files = data.Contents.filter(d => d.Key.match(/\.json$/));
        const tokens = files.map(d => d.Key.match(/^accesstokens\/([a-z0-9-]+)\.json$/)[1]);
        rv(tokens);
      }
    });
  });
}

function getPocketEntries(at) {
  const ck = process.env.POCKET_CONSUMER_KEY;
  const params = { count: 3 };
  return Pocket.get(ck, at, params).then((d) => {
    debug(`GET RESULT of ${at} IS:`, Object.keys(d.list).length);
    return d;
  }).catch((err) => {
    debug('(Skipped) Failed to get with Access Token:', at);
    debug(err);
  });
}

module.exports.prepare = (event, context, callback) => {
  debug('[prepare]>>>>');
  extractAccesstokens().then((tokens) => {
    debug(tokens);
    Promise.all(tokens.map(at => getPocketEntries(at)));
  })
    .then(() => callback(null, successResponseBuilder()))
    .catch(err => callback(null, errorResponseBuilder(err)));
};
<|endoftext|>


<commit_before>module.exports = ChatMessage;

/**
 * ChatMessage Constructor
 * @param {String|Object} message content of ChatMessage
 */
function ChatMessage(message) {
	if(typeof message === 'string') {
		this.json = { text: message };
	}else if(typeof message === 'object' && !Array.isArray(message)) {
		this.json = message;
	}else{
		throw new Error('Expected String or Object for Message argument');
	}
	this.parse();
}

/**
 * Parses the this.json property to decorate the properties of the ChatMessage.
 * Called by the Constructor
 * @return {void}
 */
ChatMessage.prototype.parse = function() {
	var json = this.json;
	// Message scope for callback functions
	var that = this;

	// There is EITHER, a text property or a translate property
	// If there is no translate property, there is no with property
	// HOWEVER! If there is a translate property, there may not be a with property
	if(typeof json.text === 'string') {
		this.text = json.text;
	}else if(typeof json.translate === 'string') {
		this.translate = json.translate;
		if(typeof json.with === 'object' || Array.isArray(json.with)) {
			this.with = [];
			json.with.forEach(function(entry) {
				if(typeof entry === 'string') {
					that.with.push(entry);
				}else if(typeof entry === 'object') {
					// Parse ChatMessage
					var subChatMessage = new ChatMessage(entry);
					that.with.push(subChatMessage);
				}
			});
		}
	}
	// Parse extra property
	// Extras are appended to the initial text
	if(typeof json.extra === 'object') {
		if(!Array.isArray(json.extra)) {
			throw new Error('Expected extra property to be an Array in ChatMessage');
		}
		this.extra = [];
		json.extra.forEach(function(entry) {
			if(typeof entry === 'string') {
				that.extra.push(entry);
			}else if(typeof entry === 'object') {
				var subChatMessage = new ChatMessage(entry);
				that.extra.push(subChatMessage);
			}
		});
	}
	// Text modifiers
	this.bold = json.bold;
	this.italic = json.italic;
	this.underlined = json.underlined;
	this.strikethrough = json.strikethrough;
	this.obfuscated = json.obfuscated;

	// Supported constants @ 2014-04-21
	var supportedColors = [ 'black', 'dark_blue', 'dark_green', 'dark_aqua', 'dark_red', 'dark_purple',
							'gold', 'gray', 'dark_gray', 'blue', 'green', 'aqua', 'red', 'light_purple',
							'yellow', 'white', 'obfuscated', 'bold', 'strikethrough', 'underlined', 'italic',
							'reset'];
	var supportedClick = ['open_url', 'open_file', 'run_command', 'suggest_command'];
	var supportedHover = ['show_text', 'show_achievement', 'show_item', 'show_entity'];

	// Parse color
	this.color = json.color;
	switch(this.color) {
		case 'obfuscated': this.obfuscated = true; this.color = null; break;
		case 'bold': this.bold = true; this.color = null; break;
		case 'strikethrough': this.strikethrough = true; this.color = null; break;
		case 'underlined': this.underlined = true; this.color = null; break;
		case 'italic': this.italic = true; this.color = null; break;
		case 'reset': this.reset = true; this.color = null; break;
	}
	if(Array.prototype.indexOf && this.color &&
		supportedColors.indexOf(this.color) === -1) {
		console.warn('ChatMessage parsed with unsupported color', this.color);
	}

	// Parse click event
	if(typeof json.clickEvent === 'object') {
		this.clickEvent = json.clickEvent;
		if(typeof this.clickEvent.action !== 'string') {
			throw new Error('ClickEvent action missing in ChatMessage');
		}else if(Array.prototype.indexOf && supportedClick.indexOf(this.clickEvent.action) === -1) {
			console.warn('ChatMessage parsed with unsupported clickEvent', this.clickEvent.action);
		}
	}

	// Parse hover event
	if(typeof json.hoverEvent === 'object') {
		this.hoverEvent = json.hoverEvent;
		if(typeof this.hoverEvent.action !== 'string') {
			throw new Error('HoverEvent action missing in ChatMessage');
		}else if(Array.prototype.indexOf && supportedHover.indexOf(this.hoverEvent.action) === -1) {
			console.warn('ChatMessage parsed with unsupported hoverEvent', this.hoverEvent.action);
		}
		// Special case
		if(this.hoverEvent.action === 'show_item') {
			this.hoverEvent.value = parseShowItemHoverEventValue(this.hoverEvent.value);
		}
	}
};

function parseShowItemHoverEventValue(string) {
  // Mojang apparently invented a syntax for this.
  // it may look like JSON at first, but it isn't, so we write our own parser.

  // examples:
  //   "{id:35,Damage:5,Count:2,tag:{display:{Name:Testing}}}"
  //   '{id:"minecraft:dirt",Damage:0s,Count:1b}'
  if(string instanceof Array)
    string=string[0];

  var index = 0;
  var result = parseAnything();
  if (index !== string.length) syntaxError();
  return result;

  function parseDict() {
    if (string[index] !== "{") syntaxError();
    index++;

    var result = {};
    var needDelimiter = false;
    while (true) {
      if (string[index] === "}") {
        index++;
        break;
      }
      if (needDelimiter) {
        if (string[index] !== ",") syntaxError();
        index++;
      }
      needDelimiter = true;
      var key = parseString();
      if(key==="") {
        index++;
        break;
      } // trailing comma
      if (string[index] !== ":") syntaxError();
      index++;
      result[key] = parseAnything();
    }
    return result;
  }
  function parseArray() {
    if (string[index] !== "[") syntaxError();
    index++;

    var result = [];
    var needDelimiter = false;
    while (true) {
      if (string[index] === "]") {
        index++;
        break;
      }
      if (needDelimiter) {
        if (string[index] !== ",") syntaxError();
        index++;
      }
      needDelimiter = true;
      var key = parseString();
      if(key==="") {
        index++;
        break;
      } // trailing comma
      key=parseInt(key);
      if (string[index] !== ":") syntaxError();
      index++;
      result[key] = parseAnything();
    }
    return result;
  }
  function parseString() {
    var result = "";
    var quoted = false;
    if (string[index] === '"') {
      index++;
      quoted = true;
    }
    while (true) {
      if (quoted) {
        if (string[index] === '"') {
          index++;
          break;
        }
      } else {
        if (/\W/.test(string[index])) break;
      }
      result += string[index];
      index++;
    }
    //if (!quoted && result === "") syntaxError(); // trailing comma
    return result;
  }
  function parseNumber() {
    var match = /^([0-9]+)[a-z]?/.exec(string.substr(index));
    if (match === null) syntaxError();
    index += match[0].length;
    return parseInt(match[1]);
  }
  function parseAnything() {
    var c = string[index];
    if (c === '"') return parseString();
    if (c === '{') return parseDict();
    if (c === '[') return parseArray();
    if ("0123456789".indexOf(c) !== -1) return parseNumber();
    // must be an unquotted string
    return parseString();
  }
  function syntaxError() {
    throw new Error("hoverEvent 'show_item' value syntax error at index " + index + ": " + string);
  }
}
function testParseShowItemHoverEventValue() {
  var assert = require("assert");
  assert.deepEqual(parseShowItemHoverEventValue('{}'), {});
  assert.deepEqual(parseShowItemHoverEventValue('{key:value}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('{key:"value"}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('{key:"va,lue"}'), {key:"va,lue"});
  assert.deepEqual(parseShowItemHoverEventValue('{k1:v1,k2:v2}'), {k1:"v1", k2:"v2"});
  assert.deepEqual(parseShowItemHoverEventValue('{number:0s}'), {number:0});
  assert.deepEqual(parseShowItemHoverEventValue('{number:123b}'), {number:123});
  assert.deepEqual(parseShowItemHoverEventValue('{nest:{}}'), {nest:{}});
  assert.deepEqual(parseShowItemHoverEventValue('{nest:{nest:{}}}'), {nest:{nest:{}}});
  assert.deepEqual(parseShowItemHoverEventValue("{id:35,Damage:5,Count:2,tag:{display:{Name:Testing}}}"),
                                               {id:35,Damage:5,Count:2,tag:{display:{Name:"Testing"}}});
  assert.deepEqual(parseShowItemHoverEventValue('{id:"minecraft:dirt",Damage:0s,Count:1b}'),
                                               {id:"minecraft:dirt",Damage:0, Count:1});

  assert.deepEqual(parseShowItemHoverEventValue('{key:value,}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('[0:v1,1:v2]'), ["v1","v2"]);
  assert.deepEqual(parseShowItemHoverEventValue('[0:"§6Last Killed: None",1:"§6Last Killer: None",2:"§6Rank: §aNovice-III",3:"§6§6Elo Rating: 1000",' +
    ']'),["§6Last Killed: None","§6Last Killer: None","§6Rank: §aNovice-III","§6§6Elo Rating: 1000"]);

  assert.deepEqual(parseShowItemHoverEventValue('{id:1s,Damage:0s,Count:1b,tag:{display:{Name:"§r§6Class' +
      ': Civilian",Lore:[0:"§6Last Killed: None",1:"§6Last Killer: None",2:"§6Rank: §aNovice-III",3:"§6§6Elo Rating: 1000",' +
          '],},},}'),
      {id:1,Damage:0, Count:1,tag:{display:{Name:"§r§6Class: Civilian",Lore:["§6Last Killed: None","§6Last Killer: None","§6Rank: §aNovice-III","§6§6Elo Rating: 1000"]}}});

  assert.deepEqual(parseShowItemHoverEventValue(['{key:value}']), {key:"value"});
}

/**
 * Returns the count of text extras and child ChatMessages
 * Does not count recursively in to the children
 * @return {Number}
 */
ChatMessage.prototype.length = function() {
	var count = 0;
	if(this.text) count++;
	else if(this.translate) count += this.with.length;

	if(this.extra) count += this.extra.length;
	return count;
};

/**
 * Returns a text part from the message
 * @param  {Number} idx Index of the part
 * @return {String}
 */
ChatMessage.prototype.getText = function(idx) {
	// If the index is not defined is is invalid, return toString
	if(typeof idx !== 'number') return this.toString();
	// If we are not a translating message, return the text
	if(this.text && idx === 0) return this.text;
	// Else return the with child if it's in range
	else if(this.with.length > idx) return this.with[idx].toString();
	// Else return the extra if it's in range
	if(this.extra && this.extra.length + (this.text ? 1 : this.with.length) > idx)
		return this.extra[idx - (this.text ? 1 : this.with.length)].toString();
	// Not sure how you want to default this
	// Undefined, an error ?
	return "";
};

/**
 * Flattens the message in to plain-text
 * @return {String}
 */
ChatMessage.prototype.toString = function() {
	var message = "";
	if(typeof this.text === 'string') message += this.text + " ";
	else {
		this.with.forEach(function(entry) {
			message += entry.toString() + " ";
		})
	}
	if(this.extra) {
		this.extra.forEach(function(entry) {
			message += entry.toString() + " ";
		})
	}
	return message.trim();
};

if (require.main === module) {
  testParseShowItemHoverEventValue();
}
<commit_msg>better fix to this.hoverEvent<commit_after>module.exports = ChatMessage;

/**
 * ChatMessage Constructor
 * @param {String|Object} message content of ChatMessage
 */
function ChatMessage(message) {
	if(typeof message === 'string') {
		this.json = { text: message };
	}else if(typeof message === 'object' && !Array.isArray(message)) {
		this.json = message;
	}else{
		throw new Error('Expected String or Object for Message argument');
	}
	this.parse();
}

/**
 * Parses the this.json property to decorate the properties of the ChatMessage.
 * Called by the Constructor
 * @return {void}
 */
ChatMessage.prototype.parse = function() {
	var json = this.json;
	// Message scope for callback functions
	var that = this;

	// There is EITHER, a text property or a translate property
	// If there is no translate property, there is no with property
	// HOWEVER! If there is a translate property, there may not be a with property
	if(typeof json.text === 'string') {
		this.text = json.text;
	}else if(typeof json.translate === 'string') {
		this.translate = json.translate;
		if(typeof json.with === 'object' || Array.isArray(json.with)) {
			this.with = [];
			json.with.forEach(function(entry) {
				if(typeof entry === 'string') {
					that.with.push(entry);
				}else if(typeof entry === 'object') {
					// Parse ChatMessage
					var subChatMessage = new ChatMessage(entry);
					that.with.push(subChatMessage);
				}
			});
		}
	}
	// Parse extra property
	// Extras are appended to the initial text
	if(typeof json.extra === 'object') {
		if(!Array.isArray(json.extra)) {
			throw new Error('Expected extra property to be an Array in ChatMessage');
		}
		this.extra = [];
		json.extra.forEach(function(entry) {
			if(typeof entry === 'string') {
				that.extra.push(entry);
			}else if(typeof entry === 'object') {
				var subChatMessage = new ChatMessage(entry);
				that.extra.push(subChatMessage);
			}
		});
	}
	// Text modifiers
	this.bold = json.bold;
	this.italic = json.italic;
	this.underlined = json.underlined;
	this.strikethrough = json.strikethrough;
	this.obfuscated = json.obfuscated;

	// Supported constants @ 2014-04-21
	var supportedColors = [ 'black', 'dark_blue', 'dark_green', 'dark_aqua', 'dark_red', 'dark_purple',
							'gold', 'gray', 'dark_gray', 'blue', 'green', 'aqua', 'red', 'light_purple',
							'yellow', 'white', 'obfuscated', 'bold', 'strikethrough', 'underlined', 'italic',
							'reset'];
	var supportedClick = ['open_url', 'open_file', 'run_command', 'suggest_command'];
	var supportedHover = ['show_text', 'show_achievement', 'show_item', 'show_entity'];

	// Parse color
	this.color = json.color;
	switch(this.color) {
		case 'obfuscated': this.obfuscated = true; this.color = null; break;
		case 'bold': this.bold = true; this.color = null; break;
		case 'strikethrough': this.strikethrough = true; this.color = null; break;
		case 'underlined': this.underlined = true; this.color = null; break;
		case 'italic': this.italic = true; this.color = null; break;
		case 'reset': this.reset = true; this.color = null; break;
	}
	if(Array.prototype.indexOf && this.color &&
		supportedColors.indexOf(this.color) === -1) {
		console.warn('ChatMessage parsed with unsupported color', this.color);
	}

	// Parse click event
	if(typeof json.clickEvent === 'object') {
		this.clickEvent = json.clickEvent;
		if(typeof this.clickEvent.action !== 'string') {
			throw new Error('ClickEvent action missing in ChatMessage');
		}else if(Array.prototype.indexOf && supportedClick.indexOf(this.clickEvent.action) === -1) {
			console.warn('ChatMessage parsed with unsupported clickEvent', this.clickEvent.action);
		}
	}

	// Parse hover event
	if(typeof json.hoverEvent === 'object') {
		this.hoverEvent = json.hoverEvent;
		if(typeof this.hoverEvent.action !== 'string') {
			throw new Error('HoverEvent action missing in ChatMessage');
		}else if(Array.prototype.indexOf && supportedHover.indexOf(this.hoverEvent.action) === -1) {
			console.warn('ChatMessage parsed with unsupported hoverEvent', this.hoverEvent.action);
		}
		// Special case
		if(this.hoverEvent.action === 'show_item') {
			this.hoverEvent.value = parseShowItemHoverEventValue(this.hoverEvent.value instanceof Array ? this.hoverEvent.value[0] : this.hoverEvent.value);
		}
	}
};

function parseShowItemHoverEventValue(string) {
  // Mojang apparently invented a syntax for this.
  // it may look like JSON at first, but it isn't, so we write our own parser.

  // examples:
  //   "{id:35,Damage:5,Count:2,tag:{display:{Name:Testing}}}"
  //   '{id:"minecraft:dirt",Damage:0s,Count:1b}'

  var index = 0;
  var result = parseAnything();
  if (index !== string.length) syntaxError();
  return result;

  function parseDict() {
    if (string[index] !== "{") syntaxError();
    index++;

    var result = {};
    var needDelimiter = false;
    while (true) {
      if (string[index] === "}") {
        index++;
        break;
      }
      if (needDelimiter) {
        if (string[index] !== ",") syntaxError();
        index++;
      }
      needDelimiter = true;
      var key = parseString();
      if(key==="") {
        index++;
        break;
      } // trailing comma
      if (string[index] !== ":") syntaxError();
      index++;
      result[key] = parseAnything();
    }
    return result;
  }
  function parseArray() {
    if (string[index] !== "[") syntaxError();
    index++;

    var result = [];
    var needDelimiter = false;
    while (true) {
      if (string[index] === "]") {
        index++;
        break;
      }
      if (needDelimiter) {
        if (string[index] !== ",") syntaxError();
        index++;
      }
      needDelimiter = true;
      var key = parseString();
      if(key==="") {
        index++;
        break;
      } // trailing comma
      key=parseInt(key);
      if (string[index] !== ":") syntaxError();
      index++;
      result[key] = parseAnything();
    }
    return result;
  }
  function parseString() {
    var result = "";
    var quoted = false;
    if (string[index] === '"') {
      index++;
      quoted = true;
    }
    while (true) {
      if (quoted) {
        if (string[index] === '"') {
          index++;
          break;
        }
      } else {
        if (/\W/.test(string[index])) break;
      }
      result += string[index];
      index++;
    }
    //if (!quoted && result === "") syntaxError(); // trailing comma
    return result;
  }
  function parseNumber() {
    var match = /^([0-9]+)[a-z]?/.exec(string.substr(index));
    if (match === null) syntaxError();
    index += match[0].length;
    return parseInt(match[1]);
  }
  function parseAnything() {
    var c = string[index];
    if (c === '"') return parseString();
    if (c === '{') return parseDict();
    if (c === '[') return parseArray();
    if ("0123456789".indexOf(c) !== -1) return parseNumber();
    // must be an unquotted string
    return parseString();
  }
  function syntaxError() {
    throw new Error("hoverEvent 'show_item' value syntax error at index " + index + ": " + string);
  }
}
function testParseShowItemHoverEventValue() {
  var assert = require("assert");
  assert.deepEqual(parseShowItemHoverEventValue('{}'), {});
  assert.deepEqual(parseShowItemHoverEventValue('{key:value}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('{key:"value"}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('{key:"va,lue"}'), {key:"va,lue"});
  assert.deepEqual(parseShowItemHoverEventValue('{k1:v1,k2:v2}'), {k1:"v1", k2:"v2"});
  assert.deepEqual(parseShowItemHoverEventValue('{number:0s}'), {number:0});
  assert.deepEqual(parseShowItemHoverEventValue('{number:123b}'), {number:123});
  assert.deepEqual(parseShowItemHoverEventValue('{nest:{}}'), {nest:{}});
  assert.deepEqual(parseShowItemHoverEventValue('{nest:{nest:{}}}'), {nest:{nest:{}}});
  assert.deepEqual(parseShowItemHoverEventValue("{id:35,Damage:5,Count:2,tag:{display:{Name:Testing}}}"),
                                               {id:35,Damage:5,Count:2,tag:{display:{Name:"Testing"}}});
  assert.deepEqual(parseShowItemHoverEventValue('{id:"minecraft:dirt",Damage:0s,Count:1b}'),
                                               {id:"minecraft:dirt",Damage:0, Count:1});

  assert.deepEqual(parseShowItemHoverEventValue('{key:value,}'), {key:"value"});
  assert.deepEqual(parseShowItemHoverEventValue('[0:v1,1:v2]'), ["v1","v2"]);
  assert.deepEqual(parseShowItemHoverEventValue('[0:"§6Last Killed: None",1:"§6Last Killer: None",2:"§6Rank: §aNovice-III",3:"§6§6Elo Rating: 1000",' +
    ']'),["§6Last Killed: None","§6Last Killer: None","§6Rank: §aNovice-III","§6§6Elo Rating: 1000"]);

  assert.deepEqual(parseShowItemHoverEventValue('{id:1s,Damage:0s,Count:1b,tag:{display:{Name:"§r§6Class' +
      ': Civilian",Lore:[0:"§6Last Killed: None",1:"§6Last Killer: None",2:"§6Rank: §aNovice-III",3:"§6§6Elo Rating: 1000",' +
          '],},},}'),
      {id:1,Damage:0, Count:1,tag:{display:{Name:"§r§6Class: Civilian",Lore:["§6Last Killed: None","§6Last Killer: None","§6Rank: §aNovice-III","§6§6Elo Rating: 1000"]}}});
}

/**
 * Returns the count of text extras and child ChatMessages
 * Does not count recursively in to the children
 * @return {Number}
 */
ChatMessage.prototype.length = function() {
	var count = 0;
	if(this.text) count++;
	else if(this.translate) count += this.with.length;

	if(this.extra) count += this.extra.length;
	return count;
};

/**
 * Returns a text part from the message
 * @param  {Number} idx Index of the part
 * @return {String}
 */
ChatMessage.prototype.getText = function(idx) {
	// If the index is not defined is is invalid, return toString
	if(typeof idx !== 'number') return this.toString();
	// If we are not a translating message, return the text
	if(this.text && idx === 0) return this.text;
	// Else return the with child if it's in range
	else if(this.with.length > idx) return this.with[idx].toString();
	// Else return the extra if it's in range
	if(this.extra && this.extra.length + (this.text ? 1 : this.with.length) > idx)
		return this.extra[idx - (this.text ? 1 : this.with.length)].toString();
	// Not sure how you want to default this
	// Undefined, an error ?
	return "";
};

/**
 * Flattens the message in to plain-text
 * @return {String}
 */
ChatMessage.prototype.toString = function() {
	var message = "";
	if(typeof this.text === 'string') message += this.text + " ";
	else {
		this.with.forEach(function(entry) {
			message += entry.toString() + " ";
		})
	}
	if(this.extra) {
		this.extra.forEach(function(entry) {
			message += entry.toString() + " ";
		})
	}
	return message.trim();
};

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


<commit_before>'use strict';

var React = require('react');
var Legend = require('../Legend');
var d3 = require('d3');

module.exports = React.createClass({

  displayName: 'LegendChart',

  propTypes: {
    children: React.PropTypes.node,
    createClass: React.PropTypes.string,
    colors: React.PropTypes.func,
    colorAccessor: React.PropTypes.func,
    data: React.PropTypes.array,
    height: React.PropTypes.node,
    legend: React.PropTypes.bool,
    legendPosition: React.PropTypes.string,
    margins: React.PropTypes.object,
    sideOffset: React.PropTypes.number,
    svgClassName: React.PropTypes.string,
    title: React.PropTypes.node,
    titleClassName: React.PropTypes.string,
    viewBox: React.PropTypes.string,
    width: React.PropTypes.node
  },

  getDefaultProps: function getDefaultProps() {
    return {
      className: 'rd3-legend-chart',
      colors: d3.scale.category20c(),
      colorAccessor: function colorAccessor(d, idx) {
        return idx;
      },
      data: [],
      legend: false,
      legendPosition: 'right',
      sideOffset: 90,
      svgClassName: 'rd3-chart',
      titleClassName: 'rd3-chart-title',
      title: ''
    };
  },
  _renderLegend: function _renderLegend() {
    var props = this.props;

    if (props.legend) {
      return React.createElement(Legend, {
        colors: props.colors,
        colorAccessor: props.colorAccessor,
        data: props.data,
        legendPosition: props.legendPosition,
        margins: props.margins,
        width: props.sideOffset
      });
    }

    return null;
  },
  _renderTitle: function _renderTitle() {
    var props = this.props;

    if (props.title !== '') {
      return React.createElement(
        'h4',
        {
          className: props.titleClassName
        },
        props.title
      );
    }
    return null;
  },
  _renderChart: function _renderChart() {
    var props = this.props;

    return React.createElement(
      'svg',
      {
        className: props.svgClassName,
        height: '100%',
        viewBox: props.viewBox,
        width: '100%'
      },
      props.children
    );
  },
  render: function render() {
    var props = this.props;

    return React.createElement(
      'div',
      {
        className: props.className,
        style: { width: props.width, height: props.height }
      },
      this._renderTitle(),
      React.createElement(
        'div',
        { style: { display: 'table', width: '100%', height: '100%' } },
        React.createElement(
          'div',
          { style: { display: 'table-cell' } },
          this._renderChart()
        ),
        React.createElement(
          'div',
          { style: { display: 'table-cell', width: props.sideOffset, verticalAlign: 'top' } },
          this._renderLegend()
        )
      )
    );
  }
});<commit_msg>increased Legend widh<commit_after>'use strict';

var React = require('react');
var Legend = require('../Legend');
var d3 = require('d3');

module.exports = React.createClass({

  displayName: 'LegendChart',

  propTypes: {
    children: React.PropTypes.node,
    createClass: React.PropTypes.string,
    colors: React.PropTypes.func,
    colorAccessor: React.PropTypes.func,
    data: React.PropTypes.array,
    height: React.PropTypes.node,
    legend: React.PropTypes.bool,
    legendPosition: React.PropTypes.string,
    margins: React.PropTypes.object,
    sideOffset: React.PropTypes.number,
    svgClassName: React.PropTypes.string,
    title: React.PropTypes.node,
    titleClassName: React.PropTypes.string,
    viewBox: React.PropTypes.string,
    width: React.PropTypes.node
  },

  getDefaultProps: function getDefaultProps() {
    return {
      className: 'rd3-legend-chart',
      colors: d3.scale.category20c(),
      colorAccessor: function colorAccessor(d, idx) {
        return idx;
      },
      data: [],
      legend: false,
      legendPosition: 'right',
      sideOffset: 90,
      svgClassName: 'rd3-chart',
      titleClassName: 'rd3-chart-title',
      title: ''
    };
  },
  _renderLegend: function _renderLegend() {
    var props = this.props;

    if (props.legend) {
      return React.createElement(Legend, {
        colors: props.colors,
        colorAccessor: props.colorAccessor,
        data: props.data,
        legendPosition: props.legendPosition,
        margins: props.margins,
        width: props.sideOffset
      });
    }

    return null;
  },
  _renderTitle: function _renderTitle() {
    var props = this.props;

    if (props.title !== '') {
      return React.createElement(
        'h4',
        {
          className: props.titleClassName
        },
        props.title
      );
    }
    return null;
  },
  _renderChart: function _renderChart() {
    var props = this.props;

    return React.createElement(
      'svg',
      {
        className: props.svgClassName,
        height: '100%',
        viewBox: props.viewBox,
        width: '100%'
      },
      props.children
    );
  },
  render: function render() {
    var props = this.props;

    return React.createElement(
      'div',
      {
        className: props.className,
        style: { width: props.width, height: props.height }
      },
      this._renderTitle(),
      React.createElement(
        'div',
        { style: { display: 'table', width: '100%', height: '100%' } },
        React.createElement(
          'div',
          { style: { display: 'table-cell' } },
          this._renderChart()
        ),
        React.createElement(
          'div',
            { style: { display: 'table-cell', width: props.sideOffset, verticalAlign: 'top',width:'100px' } },
          this._renderLegend()
        )
      )
    );
  }
});<|endoftext|>


<commit_before>  /**
   * We.js Plugin Class
   *
   *
   */
  var _ = require('lodash'),
    path = require('path'),
    fs = require('fs'),
    async = require('async');

module.exports = function (we) {
  /**
   * We.js plugin Class constructor
   *
   * @param {string} name   plugin npm pakage name
   * @param {string} projectPath project path where the plugin is instaled
   */
  function Plugin(pluginPath) {
    this.pluginPath = pluginPath;
    this.we = we;

    this.events = this.we.events;
    this.hooks = this.we.hooks;
    this.router = this.we.router;
    /**
     * Plugin Assets
     * @type {Object}
     */
    this.assets = {
      js: {},
      css: {}
    };

    this['package.json'] = require( path.join( pluginPath , 'package.json') );

    this.controllersPath = path.join( this.pluginPath,  this.controllerFolder );
    this.modelsPath = path.join( this.pluginPath,  this.modelFolder );
    this.modelHooksPath = path.join( this.pluginPath,  this.modelHookFolder );

    this.searchParsersPath = path.join( this.pluginPath, this.searchParsersFolder );
    this.searchTargetsPath = path.join( this.pluginPath, this.searchTargetsFolder );

    this.templatesPath = this.pluginPath + '/server/templates';
    this.helpersPath = this.pluginPath + '/server/helpers';
    this.resourcesPath = this.pluginPath + '/server/resources';
    this.routesPath = this.pluginPath + '/server/routes';

    this.helpers = {};
    this.layouts = {};
    this.templates = {};

    /**
     * Default plugin config object
     *
     * @type {Object}
     */
    this.configs = {};

    /**
     * Default plugin resources
     *
     * @type {Object}
     */
    this.controllers = {};
    this.models = {};
    this.routes = {};

    this.appFiles = [];
    this.appAdminFiles = [];
  }

  /**
   * Default initializer function, override in plugin.js file if need
   *
   * @param  {Object} we  we.js object
   * @param  {Function} cb callback
   */
  Plugin.prototype.init = function initPlugin(we, cb) { return cb(); }

  /**
   * Set plugin config
   * @param {Object} config
   */
  Plugin.prototype.setConfigs = function setConfigs(configs) {
    this.configs = configs;
  }

  // default plugin paths
  Plugin.prototype.controllerFolder = 'server/controllers';
  Plugin.prototype.modelFolder = 'server/models';
  Plugin.prototype.modelHookFolder = 'server/modelHooks';
  Plugin.prototype.searchParsersFolder = 'server/search/parsers';
  Plugin.prototype.searchTargetsFolder = 'server/search/targets';

  /**
   * Set plugin routes
   *
   * @param {object} routes
   */
  Plugin.prototype.setRoutes = function setRoutes(routes) {
    var routePaths = Object.keys(routes);
    var routePath;
    for (var i = routePaths.length - 1; i >= 0; i--) {
      routePath = routePaths[i];
      this.setRoute(routePath, routes[routePath]);
    }
  }

  /**
   * Set one route in plugin routes
   * @param {string} path    route path
   * @param {object} configs route configs how will be avaible as res.locals
   */
  Plugin.prototype.setRoute = function(routePath, configs) {
    this.routes[routePath] = configs;
    this.routes[routePath].path = routePath;
    this.routes[routePath].pluginPath = this.pluginPath;
  }

  Plugin.prototype.setResource = function(opts) {
    var router = this.we.router;

    var fullName = (opts.namePrefix || '') + opts.name;

    // first save resource in name or merge route options if exists
    if (!router.resourcesByName[fullName]) {
      router.resourcesByName[fullName] = opts;
    } else {
      _.merge(router.resourcesByName[fullName], opts);
    }

    if (opts.parent) {
    // is subroute

      if (!router.resourcesByName[opts.parent]) {
      // parent dont are set

        // temporary create parent resource wit subroutes attr
        router.resourcesByName[opts.parent] = { subRoutes: {} };
        // add reference to route in parent subroutes
        router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];

      } else {
      // parent resource is set

        if (!router.resourcesByName[opts.parent].subRoutes) {
          // add subRoutes object if dont are set
          router.resourcesByName[opts.parent].subRoutes = {};
        }

        if (!router.resourcesByName[opts.parent].subRoutes[fullName]) {
          router.resourcesByName[opts.parent].subRoutes[fullName] = {};
        }
        // add reference to route in parent resource subroutes
        router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];
      }

    } else {
      // is route route
      router.resources[fullName] = router.resourcesByName[fullName];
    }
  }

  /**
   * Set plugin layouts
   * @param {object} layouts
   */
  Plugin.prototype.setLayouts = function setLayouts(layouts) {
    this.layouts = layouts;
  }

  /**
   * Load all we.js plugin features
   *
   * Auto load avaible for plugins, helpers ...
   *
   */
  Plugin.prototype.loadFeatures = function loadFeatures(we, cb) {
    var self = this;

    async.parallel([
      function loadControllesAndModels(done) {
        self.loadControllers(we);
        self.loadModelHooks(we);
        self.loadModels(we);
        done();
      },
      next => self.loadSearchParsers(next),
      next => self.loadSearchTargets(next),
      function loaderHookExtensor(done) {
        we.hooks.trigger('plugin:load:features', {
          plugin: self,
          we: we
        }, done);
      },
      // load resources then the routes
      function loadResources(done) {
        self.loadResources(function(){
          self.loadRoutes(done);
        });
      }
    ], cb);
  }
  /**
   * load plugin controllers
   */
  Plugin.prototype.loadControllers = function loadControllers(we) {
    var self = this;
    var name;
    try {
      fs.readdirSync(this.controllersPath)
      .forEach(function(fileName) {
        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3);
          var attrs = require(path.join(self.controllersPath, fileName));

          attrs._controllersPath = self.controllersPath;

          we.controllers[name] =
            new we.class.Controller(attrs);
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };
  /**
   * load plugin models
   */
  Plugin.prototype.loadModels = function loadModels(we) {
    var self = this;
    var name;
    try {
      fs.readdirSync(this.modelsPath)
      .forEach(function(fileName) {
        if (fileName.endsWith('.js')) {
          // js model
          name = fileName.slice(0, -3);

          we.db.modelsConfigs[name] =
            require(path.join(self.modelsPath, fileName))(we);
        } else if(fileName.endsWith('.json')){
          // json model
          name = fileName.slice(0, -5);//

          we.db.modelsConfigs[name] =
            we.db.defineModelFromJson( require(path.join(self.modelsPath, fileName)), we);
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * load plugin model hooks
   */
  Plugin.prototype.loadModelHooks = function loadModelHooks (we) {
    var self = this;
    var name;

    try {
      fs.readdirSync(this.modelHooksPath)
      .forEach(function (fileName) {

        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3);

          we.db.modelHooks[name] =
            require(path.join(self.modelHooksPath, fileName))
            .bind({ we: we });
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * load plugin search parsers
   */
  Plugin.prototype.loadSearchParsers = function loadSearchParsers (done) {
    let name, parsers = we.router.search.parsers

    fs.readdir(this.searchParsersPath, (e, fileNames) => {
      if (e) {
        if (e.code !== 'ENOENT') return done(e)
        return done()
      }

      fileNames.forEach(fileName => {
        if (fileName.endsWith('.js')) {
          // remove .js
          name = fileName.slice(0, -3)

          parsers[name] = require(path.join(this.searchParsersPath, fileName)).bind({ we: we })
        }
      })

      done()
    })
  };

  /**
   * load plugin search targets
   */
  Plugin.prototype.loadSearchTargets = function loadSearchTargets (done) {
    let name, targets = we.router.search.targets

    fs.readdir(this.searchTargetsPath, (e, fileNames) => {
      if (e) {
        if (e.code !== 'ENOENT') return done(e)
        return done()
      }

      fileNames.forEach(fileName => {
        if (fileName.endsWith('.js')) {
          // remove .js
          name = fileName.slice(0, -3)
          // add in targets
          targets[name] = require(path.join(this.searchTargetsPath, fileName)).bind({ we: we })
        }
      })

      done()
    })
  };

  /**
   * Load route resources from folder server/resources
   *
   * @param  {Function} cb callback
   */
  Plugin.prototype.loadResources = function loadResources(cb) {
    var self = this;

    fs.readdir(this.resourcesPath , function (err, list){
      if (err) {
        if (err.code === 'ENOENT') return cb();
        throw err;
      }
      for (var i = 0; i < list.length; i++) {
       self.setResource(require(self.resourcesPath+'/'+list[i]));
      }
      cb();
    });
  }
  /**
   * Load routes from folder server/routes
   *
   * @param  {Function} cb callback
   */
  Plugin.prototype.loadRoutes = function loadRoutes(cb) {
    var self = this;

    fs.readdir(this.routesPath , function (err, list) {
      if (err) {
        if (err.code === 'ENOENT') return cb();
        throw err;
      }
      for (var i = 0; i < list.length; i++) {
       self.setRoutes(require(self.routesPath+'/'+list[i]));
      }
      cb();
    });
  }

  // -- Add css and js to we-plugin-view assets feature

  Plugin.prototype.addJs = function addJs(fileName, cfg) {
    this.assets.js[fileName] = cfg;
  }

  Plugin.prototype.addCss = function addCss(fileName, cfg) {
    this.assets.css[fileName] = cfg;
  }

  return Plugin;
}<commit_msg>fix search functions<commit_after>  /**
   * We.js Plugin Class
   *
   *
   */
  var _ = require('lodash'),
    path = require('path'),
    fs = require('fs'),
    async = require('async');

module.exports = function (we) {
  /**
   * We.js plugin Class constructor
   *
   * @param {string} name   plugin npm pakage name
   * @param {string} projectPath project path where the plugin is instaled
   */
  function Plugin(pluginPath) {
    this.pluginPath = pluginPath;
    this.we = we;

    this.events = this.we.events;
    this.hooks = this.we.hooks;
    this.router = this.we.router;
    /**
     * Plugin Assets
     * @type {Object}
     */
    this.assets = {
      js: {},
      css: {}
    };

    this['package.json'] = require( path.join( pluginPath , 'package.json') );

    this.controllersPath = path.join( this.pluginPath,  this.controllerFolder );
    this.modelsPath = path.join( this.pluginPath,  this.modelFolder );
    this.modelHooksPath = path.join( this.pluginPath,  this.modelHookFolder );

    this.searchParsersPath = path.join( this.pluginPath, this.searchParsersFolder );
    this.searchTargetsPath = path.join( this.pluginPath, this.searchTargetsFolder );

    this.templatesPath = this.pluginPath + '/server/templates';
    this.helpersPath = this.pluginPath + '/server/helpers';
    this.resourcesPath = this.pluginPath + '/server/resources';
    this.routesPath = this.pluginPath + '/server/routes';

    this.helpers = {};
    this.layouts = {};
    this.templates = {};

    /**
     * Default plugin config object
     *
     * @type {Object}
     */
    this.configs = {};

    /**
     * Default plugin resources
     *
     * @type {Object}
     */
    this.controllers = {};
    this.models = {};
    this.routes = {};

    this.appFiles = [];
    this.appAdminFiles = [];
  }

  /**
   * Default initializer function, override in plugin.js file if need
   *
   * @param  {Object} we  we.js object
   * @param  {Function} cb callback
   */
  Plugin.prototype.init = function initPlugin(we, cb) { return cb(); }

  /**
   * Set plugin config
   * @param {Object} config
   */
  Plugin.prototype.setConfigs = function setConfigs(configs) {
    this.configs = configs;
  }

  // default plugin paths
  Plugin.prototype.controllerFolder = 'server/controllers';
  Plugin.prototype.modelFolder = 'server/models';
  Plugin.prototype.modelHookFolder = 'server/modelHooks';
  Plugin.prototype.searchParsersFolder = 'server/search/parsers';
  Plugin.prototype.searchTargetsFolder = 'server/search/targets';

  /**
   * Set plugin routes
   *
   * @param {object} routes
   */
  Plugin.prototype.setRoutes = function setRoutes(routes) {
    var routePaths = Object.keys(routes);
    var routePath;
    for (var i = routePaths.length - 1; i >= 0; i--) {
      routePath = routePaths[i];
      this.setRoute(routePath, routes[routePath]);
    }
  }

  /**
   * Set one route in plugin routes
   * @param {string} path    route path
   * @param {object} configs route configs how will be avaible as res.locals
   */
  Plugin.prototype.setRoute = function(routePath, configs) {
    this.routes[routePath] = configs;
    this.routes[routePath].path = routePath;
    this.routes[routePath].pluginPath = this.pluginPath;
  }

  Plugin.prototype.setResource = function(opts) {
    var router = this.we.router;

    var fullName = (opts.namePrefix || '') + opts.name;

    // first save resource in name or merge route options if exists
    if (!router.resourcesByName[fullName]) {
      router.resourcesByName[fullName] = opts;
    } else {
      _.merge(router.resourcesByName[fullName], opts);
    }

    if (opts.parent) {
    // is subroute

      if (!router.resourcesByName[opts.parent]) {
      // parent dont are set

        // temporary create parent resource wit subroutes attr
        router.resourcesByName[opts.parent] = { subRoutes: {} };
        // add reference to route in parent subroutes
        router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];

      } else {
      // parent resource is set

        if (!router.resourcesByName[opts.parent].subRoutes) {
          // add subRoutes object if dont are set
          router.resourcesByName[opts.parent].subRoutes = {};
        }

        if (!router.resourcesByName[opts.parent].subRoutes[fullName]) {
          router.resourcesByName[opts.parent].subRoutes[fullName] = {};
        }
        // add reference to route in parent resource subroutes
        router.resourcesByName[opts.parent].subRoutes[fullName] = router.resourcesByName[fullName];
      }

    } else {
      // is route route
      router.resources[fullName] = router.resourcesByName[fullName];
    }
  }

  /**
   * Set plugin layouts
   * @param {object} layouts
   */
  Plugin.prototype.setLayouts = function setLayouts(layouts) {
    this.layouts = layouts;
  }

  /**
   * Load all we.js plugin features
   *
   * Auto load avaible for plugins, helpers ...
   *
   */
  Plugin.prototype.loadFeatures = function loadFeatures(we, cb) {
    var self = this;

    async.parallel([
      function loadControllesAndModels (done) {
        self.loadControllers(we);
        self.loadModelHooks(we);
        self.loadModels(we);
        self.loadSearchParsers(we);
        self.loadSearchTargets(we)
        done();
      },
      function loaderHookExtensor (done) {
        we.hooks.trigger('plugin:load:features', {
          plugin: self,
          we: we
        }, done);
      },
      // load resources then the routes
      function loadResources (done) {
        self.loadResources(function(){
          self.loadRoutes(done);
        });
      }
    ], cb);
  }
  /**
   * load plugin controllers
   */
  Plugin.prototype.loadControllers = function loadControllers(we) {
    var self = this;
    var name;
    try {
      fs.readdirSync(this.controllersPath)
      .forEach(function(fileName) {
        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3);
          var attrs = require(path.join(self.controllersPath, fileName));

          attrs._controllersPath = self.controllersPath;

          we.controllers[name] =
            new we.class.Controller(attrs);
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };
  /**
   * load plugin models
   */
  Plugin.prototype.loadModels = function loadModels(we) {
    var self = this;
    var name;
    try {
      fs.readdirSync(this.modelsPath)
      .forEach(function(fileName) {
        if (fileName.endsWith('.js')) {
          // js model
          name = fileName.slice(0, -3);

          we.db.modelsConfigs[name] =
            require(path.join(self.modelsPath, fileName))(we);
        } else if(fileName.endsWith('.json')){
          // json model
          name = fileName.slice(0, -5);//

          we.db.modelsConfigs[name] =
            we.db.defineModelFromJson( require(path.join(self.modelsPath, fileName)), we);
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * load plugin model hooks
   */
  Plugin.prototype.loadModelHooks = function loadModelHooks (we) {
    var self = this;
    var name;

    try {
      fs.readdirSync(this.modelHooksPath)
      .forEach(function (fileName) {

        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3);

          we.db.modelHooks[name] =
            require(path.join(self.modelHooksPath, fileName))
            .bind({ we: we });
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * load plugin search parsers
   */
  Plugin.prototype.loadSearchParsers = function loadSearchParsers () {
    var name, self = this, parsers = we.router.search.parsers

    try {
      fs.readdirSync(this.searchParsersPath)
      .forEach(function (fileName) {

        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3)

          parsers[name] = require(path.join(self.searchParsersPath, fileName)).bind({ we: we })
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * load plugin search targets
   */
  Plugin.prototype.loadSearchTargets = function loadSearchTargets () {
    var name, self = this, targets = we.router.search.targets

    try {
      fs.readdirSync(this.searchTargetsPath)
      .forEach(function (fileName) {

        if (fileName.endsWith('.js')) {
          name = fileName.slice(0, -3)

          targets[name] = require(path.join(self.searchTargetsPath, fileName)).bind({ we: we })
        }
      });
    } catch(e) {
      if (e.code !== 'ENOENT') throw e;
    }
  };

  /**
   * Load route resources from folder server/resources
   *
   * @param  {Function} cb callback
   */
  Plugin.prototype.loadResources = function loadResources(cb) {
    var self = this;

    fs.readdir(this.resourcesPath , function (err, list){
      if (err) {
        if (err.code === 'ENOENT') return cb();
        throw err;
      }
      for (var i = 0; i < list.length; i++) {
       self.setResource(require(self.resourcesPath+'/'+list[i]));
      }
      cb();
    });
  }
  /**
   * Load routes from folder server/routes
   *
   * @param  {Function} cb callback
   */
  Plugin.prototype.loadRoutes = function loadRoutes(cb) {
    var self = this;

    fs.readdir(this.routesPath , function (err, list) {
      if (err) {
        if (err.code === 'ENOENT') return cb();
        throw err;
      }
      for (var i = 0; i < list.length; i++) {
       self.setRoutes(require(self.routesPath+'/'+list[i]));
      }
      cb();
    });
  }

  // -- Add css and js to we-plugin-view assets feature

  Plugin.prototype.addJs = function addJs(fileName, cfg) {
    this.assets.js[fileName] = cfg;
  }

  Plugin.prototype.addCss = function addCss(fileName, cfg) {
    this.assets.css[fileName] = cfg;
  }

  return Plugin;
}<|endoftext|>