<commit_before>/**
 * agent.js
 * @author kazuya kawaguchi <kawakazu80@gmail.com>
 */

//
// import(s)
//

var util = require('util');
var net = require('net');
var os = require('os');
var EventEmitter = require('./util').EventEmitter;
var assert = require('./util').assert;
var logger = require('./logger');
var common = require('./common');
var Job = common.Job;
var FrameParser = common.FrameParser;
var MessageParser = common.MessageParser;
var COMMANDS = common.COMMANDS;
var COMMAND_MAPS = common.COMMAND_MAPS;

//
// global valiable(s)
//

var __frmpsr = new FrameParser();
var __msgpsr = new MessageParser();

//
// function(s)
//

function createClient (connection, frmpsr, msgpsr) {
  var _modules = {};

  var client = Object.create(EventEmitter.prototype, {
    id: {
      get: function () {
        return util.format('%s__%s', connection.remoteAddress, connection.remotePort);
      },
      enumerable: true
    },
    modules: {
      get: function () { return _modules; },
      enumerable: true
    },
    registModule: {
      value: function (module) {
        _modules[module.name] = module;
        return this;
      }
    },
    unregistModule: {
      value: function (name) {
        delete _modules[name];
        return this;
      }
    },
    unregistAllModule: {
      value: function (cb) {
        var self = this;
        Object.keys(_modules).forEach(function (name) {
          self.unregistModule(name);
          cb && cb(name);
        });
        return this;
      }
    },
    send: {
      value: function (msg, cb) {
        connection.write(frmpsr.shrink([msgpsr.pack(msg)]), function () {
          logger.debug('Client: write done (msg=%j)', msg);
          cb && cb();
        });
        return this;
      }
    },
    awake: {
      value: function () {
        if (this.type === 'worker') {
          this.send({ cmd: COMMANDS.COMMING_JOB });
        }
        return this;
      }
    },
    close: {
      value: function (active, cb) {
        var self = this;
        var active = arguments[0] || false;
        this.once('close', cb);

        connection.once('end', function () {
          logger.debug('Client: connection occured `end` event');
          if (!active) {
            connection.end();
          }
        });

        connection.once('close', function (err) {
          logger.debug('Client: connection occured `close` event (err=%s)', err);
          connection.removeAllListeners();
          self.emit('close');
          self.removeAllListeners();
        });

        if (active) {
          var msg = { cmd: COMMANDS.LEAVE };
          connection.end(frmpsr.shrink([msgpsr.pack(msg)]));
        }

        return this;
      }
    }
  });

  connection.setNoDelay();

  connection.on('error', function (err) {
    logger.debug('Client: conneciton occured `error` event (err=%s)', err);
    // TODO: should implmented error wrapping !!
    client.soonEmit('error', err);
    client.close(true, function () {
      logger.debug('Client: close done');
    });
  });

  connection.on('drain', function () {
    logger.debug('Client: connection occured `drain` event');
  });

  connection.on('data', function (data) {
    logger.debug('Client: conneciton occured `data` event (data=%s)', data);
    frmpsr.expand(data, function (frame) {
      var msg = msgpsr.unpack(frame);
      logger.debug('Client: parse message (msg=%j)', msg);
      client.emit(COMMAND_MAPS[msg.cmd], msg);
    });
  });

  return client;
}

function getModuleName (ns, func) {
  return util.format('%s%s', ns, func);
}

function createModule (ns, func) {
  var _clients = {};

  var module = Object.create({}, {
    ns: {
      value: ns,
      enumerable: true
    },
    func: {
      value: func,
      enumerable: true
    },
    name: {
      get: function () {
        return getModuleName(this.ns, this.func);
      },
      enumerable: true
    },
    clients: {
      get: function () {
        return _clients;
      },
      enumerable: true
    },
    addClient: {
      value: function (client) {
        _clients[client.id] = client;
        return this;
      }
    },
    removeClient: {
      value: function (id) {
        delete _clients[id];
        return this;
      }
    }
  });

  return module;
}

function createJob (counter) {

  var timestamp = new Date().getTime();
  var host = os.hostname();
  // TODO: should design job id.
  var _id = util.format('%d::%d::%s', counter, timestamp, host);

  var job = Object.create(EventEmitter.prototype, {
    id: {
      get: function () { return _id; },
      enumerable: true
    }
  });

  return job;
}

//
// class(es)
//

function Agent () {

  var agent = Object.create(Agent.prototype, {
    frmpsr: {
      get: function () { return __frmpsr; },
    },
    msgpsr: {
      get: function () { return __msgpsr; }
    }
  });

  return agent;
}

Agent.prototype = Object.create(EventEmitter.prototype, {
  start: {
    value: function (port, cb) {
      this.DEBUG && assert(port | cb);

      var self = this;
      var frmpsr = this.frmpsr;
      var msgpsr = this.msgpsr;
      this.clients = {};
      this.modules = {};
      var count = 0;
      var req_job_queue = [];
      var exe_jobs = {};
      var timeout_jobs = {};

      function wakeupWorker (ns, func) {
        var module = self.modules[getModuleName(ns, func)];
        for (var id in module.clients) {
          module.clients[id].awake();
        }
      }

      var options = { allowHalfOpen: false };
      var server = net.createServer(options, function (connection) {
        logger.debug('Agent: occured `connection` event (remoteAddress %s, remotePort %s)', connection.remoteAddress, connection.remotePort);

        var client = createClient(connection, frmpsr, msgpsr);

        client.on(COMMAND_MAPS[COMMANDS.JOIN], function (msg) {
          logger.info('Agent: client `JOIN` event (msg=%j)', msg);
          this.type = msg.type;
          self.clients[this.id] = this;
          this.send({ cmd: COMMANDS.JOIN, stat: 'OK' });
        });

        client.on(COMMAND_MAPS[COMMANDS.LEAVE], function (msg) {
          logger.info('Agent: client `LEAVE` event (msg=%j)', msg);
          var id = this.id;
          this.close(true, function () {
            this.unregistAllModule(function (name) {
              var module = self.modules[name];
              module.removeClient(id);
              if (Object.keys(module.clients).length === 0) {
                delete self.modules[name];
              }
            });
            delete self.clients[id];
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.SUBMIT_JOB], function (msg) {
          logger.info('Agent: client `SUBMIT_JOB` event (msg=%j)', msg);
          var job = createJob(++count);
          job.ns = msg.params.ns;
          job.func = msg.params.func;
          job.args = msg.params.args;
          job.timeout = msg.params.timeout;
          job.client = this.id;
          job.timer = null;
          logger.debug('Agent: create job (%j)', job);
          req_job_queue.push(job);
          wakeupWorker(msg.params.ns, msg.params.func);
          this.send({
            cmd: COMMANDS.CREATED_JOB,
            seq: msg.seq,
            job: {
              id: job.id,
              ns: job.ns,
              func: job.func
            }
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.REGIST_WORKER], function (msg) {
          logger.info('Agent: client `REGIST_WORKER` event (msg=%j)', msg);
          var name = getModuleName(msg.params.ns, msg.params.func);
          var module = self.modules[name] || createModule(msg.params.ns, msg.params.func);
          module.addClient(this);
          this.registModule(module);
          self.modules[name] = module;
          this.send({
            cmd: COMMANDS.REGIST_WORKER,
            stat: 'OK'
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.GET_JOB], function (msg) {
          logger.info('Agent: client `GET_JOB` event (msg=%j)', msg);
          if (req_job_queue.length === 0) {
            this.send({ cmd: COMMANDS.NO_JOB });
          } else {
            var job = req_job_queue.shift();
            exe_jobs[this.id] = job;
            logger.debug('Agent : job(id) = %s, exe_jobs(keys) = %s', job.id, Object.keys(exe_jobs));
            this.send({
              cmd: COMMANDS.ASSIGNED_JOB,
              job: {
                id: job.id,
                args: job.args || {}
              }
            });
          }
        });

        client.on(COMMAND_MAPS[COMMANDS.NO_JOB], function (msg) {
          logger.info('Agent: client `NO_JOB` event (msg=%j)', msg);
        });

        client.on(COMMAND_MAPS[COMMANDS.COMMING_JOB], function (msg) {
          logger.info('Agent: client `COMMING_JOB` event (msg=%j)', msg);
        });

        client.on(COMMAND_MAPS[COMMANDS.COMPLETE_JOB], function (msg) {
          logger.info('Agent: client `COMPLETE_JOB` event (msg=%j)', msg);
          var job = exe_jobs[this.id];
          if (job) {
            var client = self.clients[job.client];
            this.send({
              cmd: COMMANDS.COMPLETE_JOB,
              job: {
                id: job.id,
                result: msg.job.result
              }
            });
            delete exe_jobs[this.id];
            job.timer = null;
            logger.debug('Agent: exe_jobs(keys) = %j', Object.keys(exe_jobs));
          }
        });

        client.on(COMMAND_MAPS[COMMANDS.FAILED_JOB], function (msg) {
          logger.info('Agent: client `FAILED_JOB` event (msg=%j)', msg);
          var job = exe_jobs[this.id];
          if (job) {
            var client = self.clients[job.client];
            this.send({
              cmd: COMMANDS.FAILED_JOB,
              job: {
                id: job.id,
                result: msg.job.result
              }
            });
            delete exe_jobs[this.id];
            job.timer = null;
            logger.debug('Agent: exeJobs(keys) = %j', Object.keys(exeJobs));
          }
        });

      }); // end of net.createServer

      server.on('error', function (err) {
        logger.error('Agent : server occured `error` event -> %j', err);
        // TODO: should wrapped error.
        self.soonEmit('error', err);
        logger.debug('Agent : fire `error` event');
      });

      cb && this.once('start', cb);

      server.listen(port, function () {
        logger.debug('Agent : server occured `listening` event');
        self.server = server;
        self.emit('start');
        logger.debug('Agent : fire `start` event');
      });
    }
  }, // end of start
  stop: {
    value: function (cb) {
      var self = this;
      var server = this.server;

      function closeClients (clients, cb) {
        var length = Object.keys(clients).length;
        var counter = 0;
        if (length === 0) {
          process.nextTick(cb);
          return;
        }
        Object.keys(clients).forEach(function (id) {
          var client = clients[id];
          client.close(true, function () {
            logger.debug('Agent: client close');
            counter++;
            if (length === counter) {
              process.nextTick(cb);
            }
          });
          logger.debug('Agent: %s connection closing ...', id);
        });
      } // end of closeclients

      cb && this.once('stop', cb);

      closeClients(this.clients, function () {
        server.once('close', function () {
          logger.debug('Agent: server occured `close` event');
          self.emit('stop');
          server.removeAllListeners();
          delete self.server;
          logger.debug('Agent: fire `stop` event');
        });
        server.close();
      });
    } // end of stop
  }
}); // end of Agent.prototype

//
// export(s)
//

module.exports.Agent = Agent;

<commit_msg>Fixed bug.<commit_after>/**
 * agent.js
 * @author kazuya kawaguchi <kawakazu80@gmail.com>
 */

//
// import(s)
//

var util = require('util');
var net = require('net');
var os = require('os');
var EventEmitter = require('./util').EventEmitter;
var assert = require('./util').assert;
var logger = require('./logger');
var common = require('./common');
var Job = common.Job;
var FrameParser = common.FrameParser;
var MessageParser = common.MessageParser;
var COMMANDS = common.COMMANDS;
var COMMAND_MAPS = common.COMMAND_MAPS;

//
// global valiable(s)
//

var __frmpsr = new FrameParser();
var __msgpsr = new MessageParser();

//
// function(s)
//

function createClient (connection, frmpsr, msgpsr) {
  var _modules = {};

  var client = Object.create(EventEmitter.prototype, {
    id: {
      get: function () {
        return util.format('%s__%s', connection.remoteAddress, connection.remotePort);
      },
      enumerable: true
    },
    modules: {
      get: function () { return _modules; },
      enumerable: true
    },
    registModule: {
      value: function (module) {
        _modules[module.name] = module;
        return this;
      }
    },
    unregistModule: {
      value: function (name) {
        delete _modules[name];
        return this;
      }
    },
    unregistAllModule: {
      value: function (cb) {
        var self = this;
        Object.keys(_modules).forEach(function (name) {
          self.unregistModule(name);
          cb && cb(name);
        });
        return this;
      }
    },
    send: {
      value: function (msg, cb) {
        connection.write(frmpsr.shrink([msgpsr.pack(msg)]), function () {
          logger.debug('Client: write done (msg=%j)', msg);
          cb && cb();
        });
        return this;
      }
    },
    awake: {
      value: function () {
        if (this.type === 'worker') {
          this.send({ cmd: COMMANDS.COMMING_JOB });
        }
        return this;
      }
    },
    close: {
      value: function (active, cb) {
        var self = this;
        var active = arguments[0] || false;
        this.once('close', cb);

        connection.once('end', function () {
          logger.debug('Client: connection occured `end` event');
          if (!active) {
            connection.end();
          }
        });

        connection.once('close', function (err) {
          logger.debug('Client: connection occured `close` event (err=%s)', err);
          connection.removeAllListeners();
          self.emit('close');
          self.removeAllListeners();
        });

        if (active) {
          var msg = { cmd: COMMANDS.LEAVE };
          connection.end(frmpsr.shrink([msgpsr.pack(msg)]));
        }

        return this;
      }
    }
  });

  connection.setNoDelay();

  connection.on('error', function (err) {
    logger.debug('Client: conneciton occured `error` event (err=%s)', err);
    // TODO: should implmented error wrapping !!
    client.soonEmit('error', err);
    client.close(true, function () {
      logger.debug('Client: close done');
    });
  });

  connection.on('drain', function () {
    logger.debug('Client: connection occured `drain` event');
  });

  connection.on('data', function (data) {
    logger.debug('Client: conneciton occured `data` event (data=%s)', data);
    frmpsr.expand(data, function (frame) {
      var msg = msgpsr.unpack(frame);
      logger.debug('Client: parse message (msg=%j)', msg);
      client.emit(COMMAND_MAPS[msg.cmd], msg);
    });
  });

  return client;
}

function getModuleName (ns, func) {
  return util.format('%s%s', ns, func);
}

function createModule (ns, func) {
  var _clients = {};

  var module = Object.create({}, {
    ns: {
      value: ns,
      enumerable: true
    },
    func: {
      value: func,
      enumerable: true
    },
    name: {
      get: function () {
        return getModuleName(this.ns, this.func);
      },
      enumerable: true
    },
    clients: {
      get: function () {
        return _clients;
      },
      enumerable: true
    },
    addClient: {
      value: function (client) {
        _clients[client.id] = client;
        return this;
      }
    },
    removeClient: {
      value: function (id) {
        delete _clients[id];
        return this;
      }
    }
  });

  return module;
}

function createJob (counter) {

  var timestamp = new Date().getTime();
  var host = os.hostname();
  // TODO: should design job id.
  var _id = util.format('%d::%d::%s', counter, timestamp, host);

  var job = Object.create(EventEmitter.prototype, {
    id: {
      get: function () { return _id; },
      enumerable: true
    }
  });

  return job;
}

//
// class(es)
//

function Agent () {

  var agent = Object.create(Agent.prototype, {
    frmpsr: {
      get: function () { return __frmpsr; },
    },
    msgpsr: {
      get: function () { return __msgpsr; }
    }
  });

  return agent;
}

Agent.prototype = Object.create(EventEmitter.prototype, {
  start: {
    value: function (port, cb) {
      this.DEBUG && assert(port | cb);

      var self = this;
      var frmpsr = this.frmpsr;
      var msgpsr = this.msgpsr;
      this.clients = {};
      this.modules = {};
      var count = 0;
      var req_job_queue = [];
      var exe_jobs = {};
      var timeout_jobs = {};

      function wakeupWorker (ns, func) {
        var module = self.modules[getModuleName(ns, func)];
        if (module) {
          for (var id in module.clients) {
            module.clients[id].awake();
          }
        }
      }

      var options = { allowHalfOpen: false };
      var server = net.createServer(options, function (connection) {
        logger.debug('Agent: occured `connection` event (remoteAddress %s, remotePort %s)', connection.remoteAddress, connection.remotePort);

        var client = createClient(connection, frmpsr, msgpsr);

        client.on(COMMAND_MAPS[COMMANDS.JOIN], function (msg) {
          logger.info('Agent: client `JOIN` event (msg=%j)', msg);
          this.type = msg.type;
          self.clients[this.id] = this;
          this.send({ cmd: COMMANDS.JOIN, stat: 'OK' });
        });

        client.on(COMMAND_MAPS[COMMANDS.LEAVE], function (msg) {
          logger.info('Agent: client `LEAVE` event (msg=%j)', msg);
          var id = this.id;
          this.close(true, function () {
            this.unregistAllModule(function (name) {
              var module = self.modules[name];
              module.removeClient(id);
              if (Object.keys(module.clients).length === 0) {
                delete self.modules[name];
              }
            });
            delete self.clients[id];
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.SUBMIT_JOB], function (msg) {
          logger.info('Agent: client `SUBMIT_JOB` event (msg=%j)', msg);
          var job = createJob(++count);
          job.ns = msg.params.ns;
          job.func = msg.params.func;
          job.args = msg.params.args;
          job.timeout = msg.params.timeout;
          job.client = this.id;
          job.timer = null;
          logger.debug('Agent: create job (%j)', job);
          req_job_queue.push(job);
          wakeupWorker(msg.params.ns, msg.params.func);
          this.send({
            cmd: COMMANDS.CREATED_JOB,
            seq: msg.seq,
            job: {
              id: job.id,
              ns: job.ns,
              func: job.func
            }
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.REGIST_WORKER], function (msg) {
          logger.info('Agent: client `REGIST_WORKER` event (msg=%j)', msg);
          var name = getModuleName(msg.params.ns, msg.params.func);
          var module = self.modules[name] || createModule(msg.params.ns, msg.params.func);
          module.addClient(this);
          this.registModule(module);
          self.modules[name] = module;
          this.send({
            cmd: COMMANDS.REGIST_WORKER,
            stat: 'OK'
          });
        });

        client.on(COMMAND_MAPS[COMMANDS.GET_JOB], function (msg) {
          logger.info('Agent: client `GET_JOB` event (msg=%j)', msg);
          if (req_job_queue.length === 0) {
            this.send({ cmd: COMMANDS.NO_JOB });
          } else {
            var job = req_job_queue.shift();
            exe_jobs[this.id] = job;
            logger.debug('Agent : job(id) = %s, exe_jobs(keys) = %s', job.id, Object.keys(exe_jobs));
            this.send({
              cmd: COMMANDS.ASSIGNED_JOB,
              job: {
                id: job.id,
                args: job.args || {}
              }
            });
          }
        });

        client.on(COMMAND_MAPS[COMMANDS.NO_JOB], function (msg) {
          logger.info('Agent: client `NO_JOB` event (msg=%j)', msg);
        });

        client.on(COMMAND_MAPS[COMMANDS.COMMING_JOB], function (msg) {
          logger.info('Agent: client `COMMING_JOB` event (msg=%j)', msg);
        });

        client.on(COMMAND_MAPS[COMMANDS.COMPLETE_JOB], function (msg) {
          logger.info('Agent: client `COMPLETE_JOB` event (msg=%j)', msg);
          var job = exe_jobs[this.id];
          if (job) {
            var client = self.clients[job.client];
            this.send({
              cmd: COMMANDS.COMPLETE_JOB,
              job: {
                id: job.id,
                result: msg.job.result
              }
            });
            delete exe_jobs[this.id];
            job.timer = null;
            logger.debug('Agent: exe_jobs(keys) = %j', Object.keys(exe_jobs));
          }
        });

        client.on(COMMAND_MAPS[COMMANDS.FAILED_JOB], function (msg) {
          logger.info('Agent: client `FAILED_JOB` event (msg=%j)', msg);
          var job = exe_jobs[this.id];
          if (job) {
            var client = self.clients[job.client];
            this.send({
              cmd: COMMANDS.FAILED_JOB,
              job: {
                id: job.id,
                result: msg.job.result
              }
            });
            delete exe_jobs[this.id];
            job.timer = null;
            logger.debug('Agent: exeJobs(keys) = %j', Object.keys(exeJobs));
          }
        });

      }); // end of net.createServer

      server.on('error', function (err) {
        logger.error('Agent : server occured `error` event -> %j', err);
        // TODO: should wrapped error.
        self.soonEmit('error', err);
        logger.debug('Agent : fire `error` event');
      });

      cb && this.once('start', cb);

      server.listen(port, function () {
        logger.debug('Agent : server occured `listening` event');
        self.server = server;
        self.emit('start');
        logger.debug('Agent : fire `start` event');
      });
    }
  }, // end of start
  stop: {
    value: function (cb) {
      var self = this;
      var server = this.server;

      function closeClients (clients, cb) {
        var length = Object.keys(clients).length;
        var counter = 0;
        if (length === 0) {
          process.nextTick(cb);
          return;
        }
        Object.keys(clients).forEach(function (id) {
          var client = clients[id];
          client.close(true, function () {
            logger.debug('Agent: client close');
            counter++;
            if (length === counter) {
              process.nextTick(cb);
            }
          });
          logger.debug('Agent: %s connection closing ...', id);
        });
      } // end of closeclients

      cb && this.once('stop', cb);

      closeClients(this.clients, function () {
        server.once('close', function () {
          logger.debug('Agent: server occured `close` event');
          self.emit('stop');
          server.removeAllListeners();
          delete self.server;
          logger.debug('Agent: fire `stop` event');
        });
        server.close();
      });
    } // end of stop
  }
}); // end of Agent.prototype

//
// export(s)
//

module.exports.Agent = Agent;

<|endoftext|>


<commit_before>(function(root, factory) {
  if(typeof define === 'function' && define.amd) {
    define([], factory);
  } else if(typeof exports === 'object') {
    module.exports = factory();
  } else {
    root.Layzr = factory();
  }
}(this, function() {
  'use strict';

  // CONSTRUCTOR

  function Layzr(options) {
    // debounce
    this._lastScroll = 0;
    this._ticking = false;

    // options
    this._optionsSelector = options.selector || '[data-layzr]';
    this._optionsAttr = options.attr || 'data-layzr';
    this._optionsAttrRetina = options.retinaAttr || 'data-layzr-retina';
    this._optionsAttrBg = options.bgAttr || 'data-layzr-bg';
    this._optionsThreshold = options.threshold || 0;
    this._optionsCallback = options.callback || null;

    // properties
    this._retina = window.devicePixelRatio > 1;
    this._srcAttr = this._retina ? this._optionsAttrRetina : this._optionsAttr;

    // nodelist
    this._nodes = document.querySelectorAll(this._optionsSelector);

    // call to create
    this._create();
  }

  // DEBOUNCE HELPERS
  // adapted from: http://www.html5rocks.com/en/tutorials/speed/animations/

  Layzr.prototype._requestScroll = function() {
    this._lastScroll = window.scrollY || window.pageYOffset;
    this._requestTick();
  };

  Layzr.prototype._requestTick = function() {
    if(!this._ticking) {
      requestAnimationFrame(this.update.bind(this));
      this._ticking = true;
    }
  };

  // OFFSET HELPER
  // borrowed from: http://stackoverflow.com/questions/5598743/finding-elements-position-relative-to-the-document

  Layzr.prototype._getOffset = function(element) {
    var offsetTop  = 0;

    do {
      if(!isNaN(element.offsetTop)) {
        offsetTop  += element.offsetTop;
      }
    } while (element = element.offsetParent);

    return offsetTop;
  };

  // LAYZR METHODS

  Layzr.prototype._create = function() {
    // fire scroll event once
    this._requestScroll();

    // bind scroll and resize event
    window.addEventListener('scroll', this._requestScroll.bind(this), false);
    window.addEventListener('resize', this._requestScroll.bind(this), false);
  };

  Layzr.prototype._destroy = function() {
    // possibly remove attributes, and set all sources?

    // unbind scroll and resize event
    window.removeEventListener('scroll', this._requestScroll.bind(this), false);
    window.removeEventListener('resize', this._requestScroll.bind(this), false);
  };

  Layzr.prototype._inViewport = function(node) {
    // get viewport top and bottom offset
    var viewportTop = this._lastScroll;
    var viewportBottom = viewportTop + window.innerHeight;

    // get node top and bottom offset
    var elementTop = this._getOffset(node);
    var elementBottom = elementTop + node.offsetHeight;

    // calculate threshold, convert percentage to pixel value
    var threshold = (this._optionsThreshold / 100) * window.innerHeight;

    // return if element in viewport
    return elementBottom >= viewportTop - threshold && elementBottom <= viewportBottom + threshold;
  };

  Layzr.prototype._reveal = function(node) {
    // get node source
    var source = node.getAttribute(this._srcAttr) || node.getAttribute(this._optionsAttr);

    // set node src or bg image
    if(node.hasAttribute(this._optionsAttrBg)) {
      node.style.backgroundImage = 'url(' + source + ')';
    }
    else {
      node.setAttribute('src', source);
    }

    // call the callback
    if(typeof this._optionsCallback === 'function') {
      // "this" will be the node in the callback
      this._optionsCallback.call(node);
    }

    // remove node data attributes
    node.removeAttribute(this._optionsAttr);
    node.removeAttribute(this._optionsAttrRetina);
    node.removeAttribute(this._optionsAttrBg);
  };

  Layzr.prototype.updateSelector = function() {
    // update cached list of elements matching selector
    this._nodes = document.querySelectorAll(this._optionsSelector);
  };

  Layzr.prototype.update = function() {
    // cache nodelist length
    var nodesLength = this._nodes.length;

    // loop through nodes
    for(var i = 0; i < nodesLength; i++) {
      // cache node
      var node = this._nodes[i];

      // check if node has mandatory attribute
      if(node.hasAttribute(this._optionsAttr)) {
        // check if node in viewport
        if(this._inViewport(node)) {
          // reveal node
          this._reveal(node);
        }
      }
    }

    // allow for more animation frames
    this._ticking = false;
  };

  return Layzr;
}));
<commit_msg>Update to Layzr.js 1.4.3<commit_after>/*!
 * Layzr.js 1.4.2 - A small, fast, modern, and dependency-free library for lazy loading.
 * Copyright (c) 2015 Michael Cavalea - http://callmecavs.github.io/layzr.js/
 * License: MIT
 */

(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory();
  } else {
    root.Layzr = factory();
  }
}(this, function() {
'use strict';

// CONSTRUCTOR

function Layzr(options) {
  // debounce
  this._lastScroll = 0;
  this._ticking    = false;

  // options
  options = options || {};

  this._optionsContainer  = document.querySelector(options.container) || window;
  this._optionsSelector   = options.selector || '[data-layzr]';
  this._optionsAttr       = options.attr || 'data-layzr';
  this._optionsAttrRetina = options.retinaAttr || 'data-layzr-retina';
  this._optionsAttrBg     = options.bgAttr || 'data-layzr-bg';
  this._optionsAttrHidden = options.hiddenAttr || 'data-layzr-hidden';
  this._optionsThreshold  = options.threshold || 0;
  this._optionsCallback   = options.callback || null;

  // properties
  this._retina  = window.devicePixelRatio > 1;
  this._srcAttr = this._retina ? this._optionsAttrRetina : this._optionsAttr;

  // nodelist
  this._nodes = document.querySelectorAll(this._optionsSelector);

  // scroll and resize handler
  this._handlerBind = this._requestScroll.bind(this);

  // call to create
  this._create();
}

// DEBOUNCE HELPERS
// adapted from: http://www.html5rocks.com/en/tutorials/speed/animations/

Layzr.prototype._requestScroll = function() {
  if(this._optionsContainer === window) {
    this._lastScroll = window.pageYOffset;
  }
  else {
    this._lastScroll = this._optionsContainer.scrollTop + this._getOffset(this._optionsContainer);
  }

  this._requestTick();
};

Layzr.prototype._requestTick = function() {
  if(!this._ticking) {
    requestAnimationFrame(this.update.bind(this));
    this._ticking = true;
  }
};

// OFFSET HELPER
// remember, getBoundingClientRect is relative to the viewport

Layzr.prototype._getOffset = function(node) {
  return node.getBoundingClientRect().top + window.pageYOffset;
};

// HEIGHT HELPER

Layzr.prototype._getContainerHeight = function() {
  return this._optionsContainer.innerHeight
      || this._optionsContainer.offsetHeight;
}

// LAYZR METHODS

Layzr.prototype._create = function() {
  // fire scroll event once
  this._handlerBind();

  // bind scroll and resize event
  this._optionsContainer.addEventListener('scroll', this._handlerBind, false);
  this._optionsContainer.addEventListener('resize', this._handlerBind, false);
};

Layzr.prototype._destroy = function() {
  // unbind scroll and resize event
  this._optionsContainer.removeEventListener('scroll', this._handlerBind, false);
  this._optionsContainer.removeEventListener('resize', this._handlerBind, false);
};

Layzr.prototype._inViewport = function(node) {
  // get viewport top and bottom offset
  var viewportTop = this._lastScroll;
  var viewportBottom = viewportTop + this._getContainerHeight();

  // get node top and bottom offset
  var nodeTop = this._getOffset(node);
  var nodeBottom = nodeTop + this._getContainerHeight();

  // calculate threshold, convert percentage to pixel value
  var threshold = (this._optionsThreshold / 100) * window.innerHeight;

  // return if node in viewport
  return nodeBottom >= viewportTop - threshold
      && nodeTop <= viewportBottom + threshold
      && !node.hasAttribute(this._optionsAttrHidden);
};

Layzr.prototype._reveal = function(node) {
  // get node source
  var source = node.getAttribute(this._srcAttr) || node.getAttribute(this._optionsAttr);

  // set node src or bg image
  if(node.hasAttribute(this._optionsAttrBg)) {
    node.style.backgroundImage = 'url(' + source + ')';
  }
  else {
    node.setAttribute('src', source);
  }

  // call the callback
  if(typeof this._optionsCallback === 'function') {
    // "this" will be the node in the callback
    this._optionsCallback.call(node);
  }

  // remove node data attributes
  node.removeAttribute(this._optionsAttr);
  node.removeAttribute(this._optionsAttrRetina);
  node.removeAttribute(this._optionsAttrBg);
  node.removeAttribute(this._optionsAttrHidden);
};

Layzr.prototype.updateSelector = function() {
  // update cached list of nodes matching selector
  this._nodes = document.querySelectorAll(this._optionsSelector);
};

Layzr.prototype.update = function() {
  // cache nodelist length
  var nodesLength = this._nodes.length;

  // loop through nodes
  for(var i = 0; i < nodesLength; i++) {
    // cache node
    var node = this._nodes[i];

    // check if node has mandatory attribute
    if(node.hasAttribute(this._optionsAttr)) {
      // check if node in viewport
      if(this._inViewport(node)) {
        // reveal node
        this._reveal(node);
      }
    }
  }

  // allow for more animation frames
  this._ticking = false;
};

return Layzr;
}));
<|endoftext|>


<commit_before>const debug = require('debug')('wechat');
const Promise = require('bluebird');
const qs = require("querystring");
const _ = require('lodash');

const util = require('./utils');
const config = require('./config');

const wxConfig = config.getDefaultConfiguration();

const REDIRECT_HASH = '#wechat_redirect';
const oauthScope = {
  BASE: 'snsapi_base',
  USER_INFO: 'snsapi_userinfo',
};

const oAuthDefaultParams = {
  redirect_uri: '',
  response_type: 'code',
};

class OAuth {

  /**
   * OAuth class
   * @constructor
   * @param {object=} options
   * @return {OAuth} OAuth instance
   */
  constructor (options) {

    config.checkPassedConfiguration(options);
    /**
     *
     * {
     *   "access_token":"ACCESS_TOKEN",
     *   "expires_in":7200,
     *   "refresh_token":"REFRESH_TOKEN",
     *   "openid":"OPENID",
     *   "scope":"SCOPE",
     * }
     * @type {object}
     */
    this.oauthAccessToken = {};

    this.wechatConfig = _.isEmpty(options) ? wxConfig : Object.assign({}, wxConfig, options);

    this.oAuthUrl = this.wechatConfig.oAuthUrl + '?';

    this.setDefaultOauthUrl();

    setInterval(() => this.refreshAccessToken()
      , options.refreshAccessTokenInterval || 1000 * 60 * 158);
  }

  /**
   * Get wechat user profile based on the access token
   * @param {object} tokenInfo access token info received based on the code(passed by the wechat server to the redirect_uri)
   * @param {boolean=} withToken if true, the access token info will be merged to the resolved user profile object
   * @return {Promise}
   */
  getUserInfoWithToken (tokenInfo, withToken) {
    return util.sendWechatRequest({
        url: '/sns/userinfo',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          access_token: tokenInfo.access_token,
          openid: tokenInfo.openid,
          lang: 'zh_CN',
        },
      })
      .then((data) => {
        debug('user info received');
        return withToken ? Object.assign({}, tokenInfo, data) : data;
      })
      .catch((reason) => {
        debug('get user info failed!');
        return Promise.reject(reason);
      });
  }

  /**
   * Set the expire time starting from now for the cached access token
   * @param {object} tokenInfo
   */
  static setExpirationTime (tokenInfo) {
    if(!tokenInfo.expires_in) return;
    const now = Date.now();
    tokenInfo.expirationTime = now + ((tokenInfo.expires_in - 60) * 1000); //minus 60s to expire
  }

  /**
   * Generate redirect url for use wechat oauth page
   * @param {string} myRedirectUrl
   * @param {boolean=} baseInfo , get base info api, default false which gets userinfo
   * @return {string} generated redirect uri
   */
  generateOauthUrl (myRedirectUrl, baseInfo) {
    let url = this.oAuthUrl;
    const tempObj = {
      appid: this.wechatConfig.appId,
    };
    const oauthState = this.wechatConfig.oAuthState || 'userAuth';
    const tempOAuthParams = Object.assign(tempObj, oAuthDefaultParams, {
      redirect_uri: myRedirectUrl,
      state: oauthState,
    });
    tempOAuthParams.scope = baseInfo ? oauthScope.BASE : oauthScope.USER_INFO;

    const keys = Object.keys(tempOAuthParams);
    //sort the keys for correct order on url query
    keys.sort();
    const oauthParams = {};
    keys.forEach(key => oauthParams[key] = tempOAuthParams[key]);

    url += qs.stringify(oauthParams);
    url += REDIRECT_HASH;
    return url;
  }

  /**
   * Get wechat user base info
   * @param code code included in the redirect url
   */
  getUserBaseInfo (code) {
    return this.getOAuthAccessToken(code).then(data => data);
  }

  /**
   * Get wechat user info, including nickname, openid, avatar, etc...
   * @param code
   * @param withToken return token info together with the profile
   * @return {Promise}
   */
  getUserInfo (code, withToken) {
    return this.getOAuthAccessToken(code)
      .then((tokenInfo) => {
        return this.getUserInfoWithToken(tokenInfo, withToken);
      })
      .catch((reason) => {
        return Promise.reject(reason);
      })
      ;
  }

  /**
   * {
   *   code: '', // code query from wechat redirect
   *   returnToken: false, //if token info should be returned together with user profile
   * }
   * @param {object} options
   */
  getUserInfoByOptions (options={}) {
    return this.getUserInfo(options.code, options.returnToken);
  }

  /**
   * Get oauth access token
   * @param {string} code
   * @return {Promise}
   */
  getOAuthAccessToken (code) {
    if(!code) {
      if(!this.isTokenExpired()) {
        return Promise.resolve(this.oauthAccessToken);
      }
      return this.refreshAccessToken();
    }
    debug('getting new oauth access token...');
    return util.sendWechatRequest({
        url: '/sns/oauth2/access_token',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          secret: this.wechatConfig.appSecret,
          code: code,
          grant_type: 'authorization_code',
        },
      })
      .then((data) => {
        this.oauthAccessToken = data;
        OAuth.setExpirationTime(this.oauthAccessToken);
        return data;
      })
      .catch((reason) => {
        debug('get oauth access token failed!');
        return Promise.reject(reason);
      });
  }

  /**
   * Refresh access token with the cached refresh_token over the wechat server
   * @returns {Promise}
   */
  refreshAccessToken () {
    return util.sendWechatRequest({
        url: '/sns/oauth2/refresh_token',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          refresh_token: this.oauthAccessToken.refresh_token,
          grant_type: 'refresh_token',
        },
      })
      .then((data) => {
        this.oauthAccessToken = data;
        OAuth.setExpirationTime(this.oauthAccessToken);
        return data;
      })
      .catch((err) => {
        debug('please get the new code!');
        return Promise.reject(err);
      });
  }

  /**
   * Check if cached token is valid over the wechat server
   * @return {Promise}
   */
  isAccessTokenValid () {
    return util.sendWechatRequest({
        url: '/sns/auth',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          access_token: this.oauthAccessToken.access_token,
        },
      });
  }

  /**
   * Set default wechat oauth url for the instance
   */
  setDefaultOauthUrl () {
    let temp = this.wechatConfig.wechatRedirectUrl;
    if (!temp) {
      temp = this.wechatConfig.wechatRedirectHost + '/wechat/oauth-callback';
    }
    this.snsUserInfoUrl = this.generateOauthUrl(temp);
    this.snsUserBaseUrl = this.generateOauthUrl(temp, true);
  }

  /**
   * Check if cached token is expired
   * @return {boolean}
   */
  isTokenExpired () {
    if(!this.oauthAccessToken.expirationTime) return true;
    return Date.now() - this.oauthAccessToken.expirationTime >= 0;
  }

}

module.exports = OAuth;<commit_msg>revert code, fix getting same cached token for different users<commit_after>const debug = require('debug')('wechat');
const Promise = require('bluebird');
const qs = require("querystring");
const _ = require('lodash');

const util = require('./utils');
const config = require('./config');

const wxConfig = config.getDefaultConfiguration();

const REDIRECT_HASH = '#wechat_redirect';
const oauthScope = {
  BASE: 'snsapi_base',
  USER_INFO: 'snsapi_userinfo',
};

const oAuthDefaultParams = {
  redirect_uri: '',
  response_type: 'code',
};

class OAuth {

  /**
   * OAuth class
   * @constructor
   * @param {object=} options
   * @return {OAuth} OAuth instance
   */
  constructor (options) {

    config.checkPassedConfiguration(options);
    /**
     *
     * {
     *   "access_token":"ACCESS_TOKEN",
     *   "expires_in":7200,
     *   "refresh_token":"REFRESH_TOKEN",
     *   "openid":"OPENID",
     *   "scope":"SCOPE",
     * }
     * @type {object}
     */
    this.oauthAccessToken = {};

    this.wechatConfig = _.isEmpty(options) ? wxConfig : Object.assign({}, wxConfig, options);

    this.oAuthUrl = this.wechatConfig.oAuthUrl + '?';

    this.setDefaultOauthUrl();

    setInterval(() => this.refreshAccessToken()
      , options.refreshAccessTokenInterval || 1000 * 60 * 158);
  }

  /**
   * Get wechat user profile based on the access token
   * @param {object} tokenInfo access token info received based on the code(passed by the wechat server to the redirect_uri)
   * @param {boolean=} withToken if true, the access token info will be merged to the resolved user profile object
   * @return {Promise}
   */
  getUserInfoWithToken (tokenInfo, withToken) {
    return util.sendWechatRequest({
        url: '/sns/userinfo',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          access_token: tokenInfo.access_token,
          openid: tokenInfo.openid,
          lang: 'zh_CN',
        },
      })
      .then((data) => {
        debug('user info received');
        return withToken ? Object.assign({}, tokenInfo, data) : data;
      })
      .catch((reason) => {
        debug('get user info failed!');
        return Promise.reject(reason);
      });
  }

  /**
   * Set the expire time starting from now for the cached access token
   * @param {object} tokenInfo
   */
  static setExpirationTime (tokenInfo) {
    if(!tokenInfo.expires_in) return;
    const now = Date.now();
    tokenInfo.expirationTime = now + ((tokenInfo.expires_in - 60) * 1000); //minus 60s to expire
  }

  /**
   * Generate redirect url for use wechat oauth page
   * @param {string} myRedirectUrl
   * @param {boolean=} baseInfo , get base info api, default false which gets userinfo
   * @return {string} generated redirect uri
   */
  generateOauthUrl (myRedirectUrl, baseInfo) {
    let url = this.oAuthUrl;
    const tempObj = {
      appid: this.wechatConfig.appId,
    };
    const oauthState = this.wechatConfig.oAuthState || 'userAuth';
    const tempOAuthParams = Object.assign(tempObj, oAuthDefaultParams, {
      redirect_uri: myRedirectUrl,
      state: oauthState,
    });
    tempOAuthParams.scope = baseInfo ? oauthScope.BASE : oauthScope.USER_INFO;

    const keys = Object.keys(tempOAuthParams);
    //sort the keys for correct order on url query
    keys.sort();
    const oauthParams = {};
    keys.forEach(key => oauthParams[key] = tempOAuthParams[key]);

    url += qs.stringify(oauthParams);
    url += REDIRECT_HASH;
    return url;
  }

  /**
   * Get wechat user base info
   * @param code code included in the redirect url
   */
  getUserBaseInfo (code) {
    return this.getOAuthAccessToken(code).then(data => data);
  }

  /**
   * Get wechat user info, including nickname, openid, avatar, etc...
   * @param code
   * @param withToken return token info together with the profile
   * @return {Promise}
   */
  getUserInfo (code, withToken) {
    return this.getOAuthAccessToken(code)
      .then((tokenInfo) => {
        return this.getUserInfoWithToken(tokenInfo, withToken);
      })
      .catch((reason) => {
        return Promise.reject(reason);
      })
      ;
  }

  /**
   * {
   *   code: '', // code query from wechat redirect
   *   returnToken: false, //if token info should be returned together with user profile
   * }
   * @param {object} options
   */
  getUserInfoByOptions (options={}) {
    return this.getUserInfo(options.code, options.returnToken);
  }

  /**
   * Get oauth access token
   * @param {string} code
   * @return {Promise}
   */
  getOAuthAccessToken (code) {
    if(!code) {
      return Promise.reject(new Error('"code" not specified!'));
    }
    debug('getting new oauth access token...');
    return util.sendWechatRequest({
        url: '/sns/oauth2/access_token',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          secret: this.wechatConfig.appSecret,
          code: code,
          grant_type: 'authorization_code',
        },
      })
      .then((data) => {
        this.oauthAccessToken = data;
        OAuth.setExpirationTime(this.oauthAccessToken);
        return data;
      })
      .catch((reason) => {
        debug('get oauth access token failed!');
        return Promise.reject(reason);
      });
  }

  /**
   * Refresh access token with the cached refresh_token over the wechat server
   * @returns {Promise}
   */
  refreshAccessToken () {
    return util.sendWechatRequest({
        url: '/sns/oauth2/refresh_token',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          refresh_token: this.oauthAccessToken.refresh_token,
          grant_type: 'refresh_token',
        },
      })
      .then((data) => {
        this.oauthAccessToken = data;
        OAuth.setExpirationTime(this.oauthAccessToken);
        return data;
      })
      .catch((err) => {
        debug('please get the new code!');
        return Promise.reject(err);
      });
  }

  /**
   * Check if cached token is valid over the wechat server
   * @return {Promise}
   */
  isAccessTokenValid () {
    return util.sendWechatRequest({
        url: '/sns/auth',
        baseUrl: this.wechatConfig.apiUrl,
        qs: {
          appid: this.wechatConfig.appId,
          access_token: this.oauthAccessToken.access_token,
        },
      });
  }

  /**
   * Set default wechat oauth url for the instance
   */
  setDefaultOauthUrl () {
    let temp = this.wechatConfig.wechatRedirectUrl;
    if (!temp) {
      temp = this.wechatConfig.wechatRedirectHost + '/wechat/oauth-callback';
    }
    this.snsUserInfoUrl = this.generateOauthUrl(temp);
    this.snsUserBaseUrl = this.generateOauthUrl(temp, true);
  }

  /**
   * Check if cached token is expired
   * @return {boolean}
   */
  isTokenExpired () {
    if(!this.oauthAccessToken.expirationTime) return true;
    return Date.now() - this.oauthAccessToken.expirationTime >= 0;
  }

}

module.exports = OAuth;<|endoftext|>


<commit_before>/*jshint node: true, mocha: true*/
/**
 * @author lattmann / https://github.com/lattmann
 */

describe('configuration', function () {
    'use strict';

    var should = require('chai').should(),
        oldNodeEnv = process.env.NODE_ENV || '',
        path = require('path'),
        getClientConfig = require('../../config/getclientconfig'),
        configPath = path.join(__dirname, '..', '..', 'config'),
        unloadConfigs = function () {
            // clear the cached files
            var key,
                i,
                modulesToUnload = [];

            for (key in require.cache) {
                if (key.indexOf(configPath) > -1) {
                    modulesToUnload.push(key);
                }
            }

            for (i = 0; i < modulesToUnload.length; i += 1) {
                delete require.cache[modulesToUnload[i]];
            }
        };

    before(function () {

    });

    beforeEach(function () {
        unloadConfigs();
    });

    after(function () {
        unloadConfigs();
        process.env.NODE_ENV = oldNodeEnv;
        // restore config
        require('../../config');
    });

    it('should load global as a default config', function () {
        var config,
            configDefault = require('../../config/config.default.js');
        process.env.NODE_ENV = '';
        config = require('../../config');

        config.should.deep.equal(configDefault);
    });

    it('should load test config', function () {
        var config,
            configTest = require('../../config/config.test.js');
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        config.should.deep.equal(configTest);
    });

    it('should be serializable', function () {
        var config;
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        config.should.deep.equal(JSON.parse(JSON.stringify(config)));
    });

    it('should throw if configuration is malformed', function () {
        var config;
        process.env.NODE_ENV = 'malformed';

        (function () {
            config = require('../../config');
        }).should.throw(Error);
    });

    it('clientconfig should not expose mongo', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.hasOwnProperty('mongo'), false);
    });

    it('clientconfig should not expose executor.nonce', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.executor.hasOwnProperty('nonce'), false);
    });

    it('clientconfig should not expose server.sessionCookieSecret', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.server.hasOwnProperty('sessionCookieSecret'), false);
    });

    it('clientconfig should not expose server.https.certificateFile', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.server.https.hasOwnProperty('certificateFile'), false);
    });
});<commit_msg>Add configuration structure tests.<commit_after>/*jshint node: true, mocha: true*/
/**
 * @author lattmann / https://github.com/lattmann
 */

describe('configuration', function () {
    'use strict';

    var should = require('chai').should(),
        oldNodeEnv = process.env.NODE_ENV || '',
        path = require('path'),
        getClientConfig = require('../../config/getclientconfig'),
        configPath = path.join(__dirname, '..', '..', 'config'),
        validateConfig = require('../../config/validator'),
        unloadConfigs = function () {
            // clear the cached files
            var key,
                i,
                modulesToUnload = [];

            for (key in require.cache) {
                if (key.indexOf(configPath) > -1) {
                    modulesToUnload.push(key);
                }
            }

            for (i = 0; i < modulesToUnload.length; i += 1) {
                delete require.cache[modulesToUnload[i]];
            }
        };

    before(function () {

    });

    beforeEach(function () {
        unloadConfigs();
    });

    after(function () {
        unloadConfigs();
        process.env.NODE_ENV = oldNodeEnv;
        // restore config
        require('../../config');
    });

    it('should load global as a default config', function () {
        var config,
            configDefault = require('../../config/config.default.js');
        process.env.NODE_ENV = '';
        config = require('../../config');

        config.should.deep.equal(configDefault);
    });

    it('should load test config', function () {
        var config,
            configTest = require('../../config/config.test.js');
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        config.should.deep.equal(configTest);
    });

    it('should be serializable', function () {
        var config;
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        config.should.deep.equal(JSON.parse(JSON.stringify(config)));
    });

    it('should throw if configuration is malformed', function () {
        var config;
        process.env.NODE_ENV = 'malformed';

        (function () {
            config = require('../../config');
        }).should.throw(Error);
    });

    it('should throw if configuration has extra key', function () {
        var config;
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        (function () {
            config.extraKey = 'something';
            validateConfig(config);
        }).should.throw(Error);
    });

    it('should throw if plugin.basePaths is not an array', function () {
        var config;
        process.env.NODE_ENV = 'test';
        config = require('../../config');

        (function () {
            config.plugin.basePaths = 'something';
            validateConfig(config);
        }).should.throw(Error);
    });

    it('clientconfig should not expose mongo', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.hasOwnProperty('mongo'), false);
    });

    it('clientconfig should not expose executor.nonce', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.executor.hasOwnProperty('nonce'), false);
    });

    it('clientconfig should not expose server.sessionCookieSecret', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.server.hasOwnProperty('sessionCookieSecret'), false);
    });

    it('clientconfig should not expose server.https.certificateFile', function () {
        var config,
            clientConfig;
        process.env.NODE_ENV = '';
        config = require('../../config');
        clientConfig = getClientConfig(config);

        should.equal(clientConfig.server.https.hasOwnProperty('certificateFile'), false);
    });
});<|endoftext|>


<commit_before>(function(angular){

    var app = angular.module('edit-lesson');

    app.controller('EditLessonController', ['$scope', 'Course', 'CourseProfessor', 'Lesson', 'youtubePlayerApi',
        function($scope, Course, CourseProfessor, Lesson, youtubePlayerApi){
            $scope.errors = {};
            var httpErrors = {
                '400': 'Os campos não foram preenchidos corretamente.',
                '403': 'Você não tem permissão para ver conteúdo nesta página.',
                '404': 'Este curso não existe!'
            };

            $scope.course = new Course();
            $scope.lesson = new Lesson();
            $scope.currentUnit = null;
            $scope.courseProfessors = [];

            $scope.setLesson = function(l) {
                $scope.lesson = l;
                if(l.units.length > 0) {
                    $scope.currentUnit = l.units[0];
                }
            };

            // vv como faz isso de uma formula angular ?
            var match = document.location.href.match(/courses\/(\d+)\/lessons\/(new|\d+)/);
            if( match ) {
                $scope.course.$get({id: match[1]})
                    .then(function(course){
                        $scope.courseProfessors = CourseProfessor.query({ course: course.id });
                        return $scope.courseProfessors.$promise;
                    });

                Lesson.query({course__id: match[1]}).$promise
                    .then(function(lessons){
                        $scope.lessons = lessons;
                        lessons.forEach(function(lesson){
                            if(lesson.id === parseInt(match[2], 10)) {
                                $scope.setLesson(lesson);
                            }
                        });
                    })
                    .catch(function(resp){
                        $scope.alert.error(httpErrors[resp.status.toString()]);
                    });
            }
            // ^^ como faz isso de uma formula angular ?

            var player;
            $scope.playerReady = false;
            youtubePlayerApi.loadPlayer().then(function(p){
                player = p;
                $scope.playerReady = true;
            });

            $scope.$watch('currentUnit.video.youtube_id', function(vid, oldVid){
                if(!vid || vid === oldVid) return;
                if(player) player.cueVideoById(vid);
                //VideoData.load(vid).then(function(data){
                //    $scope.course.intro_video.name = data.entry.title.$t;
                //});
            });
        }
    ]);

})(window.angular);
<commit_msg>clean variable on lesson change<commit_after>(function(angular){

    var app = angular.module('edit-lesson');

    app.controller('EditLessonController', ['$scope', 'Course', 'CourseProfessor', 'Lesson', 'youtubePlayerApi',
        function($scope, Course, CourseProfessor, Lesson, youtubePlayerApi){
            $scope.errors = {};
            var httpErrors = {
                '400': 'Os campos não foram preenchidos corretamente.',
                '403': 'Você não tem permissão para ver conteúdo nesta página.',
                '404': 'Este curso não existe!'
            };

            $scope.course = new Course();
            $scope.lesson = new Lesson();
            $scope.currentUnit = null;
            $scope.courseProfessors = [];

            $scope.setLesson = function(l) {
                $scope.lesson = l;
                if(l.units.length > 0) {
                    $scope.currentUnit = l.units[0];
                } else {
                    $scope.currentUnit = null;
                }
            };

            // vv como faz isso de uma formula angular ?
            var match = document.location.href.match(/courses\/(\d+)\/lessons\/(new|\d+)/);
            if( match ) {
                $scope.course.$get({id: match[1]})
                    .then(function(course){
                        $scope.courseProfessors = CourseProfessor.query({ course: course.id });
                        return $scope.courseProfessors.$promise;
                    });

                Lesson.query({course__id: match[1]}).$promise
                    .then(function(lessons){
                        $scope.lessons = lessons;
                        lessons.forEach(function(lesson){
                            if(lesson.id === parseInt(match[2], 10)) {
                                $scope.setLesson(lesson);
                            }
                        });
                    })
                    .catch(function(resp){
                        $scope.alert.error(httpErrors[resp.status.toString()]);
                    });
            }
            // ^^ como faz isso de uma formula angular ?

            var player;
            $scope.playerReady = false;
            youtubePlayerApi.loadPlayer().then(function(p){
                player = p;
                $scope.playerReady = true;
            });

            $scope.$watch('currentUnit.video.youtube_id', function(vid, oldVid){
                if(!vid || vid === oldVid) return;
                if(player) player.cueVideoById(vid);
                //VideoData.load(vid).then(function(data){
                //    $scope.course.intro_video.name = data.entry.title.$t;
                //});
            });
        }
    ]);

})(window.angular);
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash'),
    os = require('os'),
    Alias = require('./alias.js'),
    utils = require('./utils.js');

/*----------------------------------------------------------------------------*/

function cleanValue(string) {
  string = string == null ? '' : String(string);
  return _.trim(string.replace(/(?:^|\n)[\t ]*\*[\t ]*/g, ' '));
}

function getMultilineValue(string, tagName) {
  var prelude = tagName == 'description' ? '^ */\\*\\*(?: *\\n *\\* *)?' : ('^ *\\*[\\t ]*@' + _.escapeRegExp(tagName) + '\\b'),
      postlude = '(?=\\*\\s+\\@[a-z]|\\*/)',
      result = _.result(RegExp(prelude + '([\\s\\S]*?)' + postlude, 'gm').exec(string), 1, '');

  return _.trim(result.replace(RegExp('(?:^|\\n)[\\t ]*\\*[\\t ]' + (tagName == 'example' ? '?' : '*'), 'g'), '\n'));
}

function getValue(string, tagName) {
  tagName = tagName == 'member' ? (tagName + '(?:Of)?') : _.escapeRegExp(tagName);
  var result = _.result(RegExp('^ *\\*[\\t ]*@' + tagName + '\\s+(.+)', 'm').exec(string), 1, '');
  return cleanValue(result);
}

function hasTag(string, tagName) {
  tagName = tagName == '*' ? '\\w+' : _.escapeRegExp(tagName);
  return RegExp('^ *\\*[\\t ]*@' + tagName + '\\b', 'm').test(string);
}

/*----------------------------------------------------------------------------*/

/**
 * The Entry constructor.
 *
 * @constructor
 * @param {string} entry The documentation entry to analyse.
 * @param {string} source The source code.
 * @param {string} [lang='js'] The language highlighter used for code examples.
 */
function Entry(entry, source, lang) {
  this.entry = entry;
  this.lang = lang == null ? 'js' : lang;
  this.source = source.replace(os.EOL, '\n');
  this.getCall = _.memoize(getCall);
  this.getCategory = _.memoize(getCategory);
  this.getDesc = _.memoize(getDesc);
  this.getExample = _.memoize(getExample);
  this.isAlias = _.memoize(isAlias);
  this.isCtor = _.memoize(isCtor);
  this.isFunction = _.memoize(isFunction);
  this.isLicense = _.memoize(isLicense);
  this.isPlugin = _.memoize(isPlugin);
  this.isPrivate = _.memoize(isPrivate);
  this.isStatic = _.memoize(isStatic);
  this.getLineNumber = _.memoize(getLineNumber);
  this.getName = _.memoize(getName);
  this.getReturns = _.memoize(getReturns);
  this.getType = _.memoize(getType);
  this._aliases = this._members = this._params = undefined;
}

/**
 * Extracts the documentation entries from source code.
 *
 * @static
 * @memberOf Entry
 * @param {string} source The source code.
 * @returns {Array} The array of entries.
 */
function getEntries(source) {
  source = source == null ? '' : String(source);
  return source.match(/\/\*\*(?![-!])[\s\S]*?\*\/\s*.+/g) || [];
}

/**
 * Extracts the entry's `alias` objects.
 *
 * @memberOf Entry
 * @param {number} index The index of the array value to return.
 * @returns {Array|string} The entry's `alias` objects.
 */
function getAliases(index) {
  if (this._aliases === undefined) {
    var result = _.result(/\*[\t ]*@alias\s+(.+)/.exec(this.entry), 1);
    if (result) {
      result = result.replace(/(?:^|\n)[\t ]*\*[\t ]*/, ' ').trim();
      result = result.split(/,\s*/);
      result.sort(utils.compareNatural);

      result = _.map(result, function(value) {
        return new Alias(value, this);
      }, this);
    }
    this._aliases = result;
  }
  return index != null
    ? this._aliases[index]
    : this._aliases;
}

/**
 * Extracts the function call from the entry.
 *
 * @memberOf Entry
 * @returns {string} The function call.
 */
function getCall() {
  var result = /\*\/\s*(?:function ([^(]*)|(.*?)(?=[:=,]|return\b))/.exec(this.entry);
  if (result) {
    result = (result[1] || result[2]).split('.').pop();
    result = _.trim(_.trim(result), "'").split('var ').pop();
    result = _.trim(result);
  }
  // resolve name
  // avoid this.getName() because it calls this.getCall()
  var name = _.result(/\*[\t ]*@name\s+(.+)/.exec(this.entry), 1, result || '');
  if (!this.isFunction()) {
    return name;
  }
  var params = this.getParams(),
      paramNames = [];

  // compile function call syntax
  result = [result];
  _.each(params, function(param) {
    // skip params that are properties of other params (e.g. `options.leading`)
    var parentParam = _.result(/\w+(?=\.[\w.]+)/.exec(param[1]), 0);
    if (!_.contains(paramNames, parentParam)) {
      result.push(param[1]);
    }
    paramNames.push(param[1].replace(/^\[|\]/, ''));
  });

  // format
  return name + '(' + result.slice(1).join(', ') + ')';
}

/**
 * Extracts the entry's `category` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `category` data.
 */
function getCategory() {
  var result = _.result(/\*[\t ]*@category\s+(.+)/.exec(this.entry), 1, '');
  return result
    ? cleanValue(result)
    : (this.getType() == 'Function' ? 'Methods' : 'Properties');
}

/**
 * Extracts the entry's description.
 *
 * @memberOf Entry
 * @returns {string} The entry's description.
 */
function getDesc() {
  var result = getMultilineValue(this.entry, 'description');
  if (!result) {
    return result;
  }
  result = _.trim(result
    .replace(/:\n[\t ]*\*[\t ]*/g, ':
\n')
    .replace(/(?:^|\n)[\t ]*\*\n[\t ]*\*[\t ]*/g, '\n\n')
    .replace(/(?:^|\n)[\t ]*\*[\t ]?/g, ' ')
  );

  var type = this.getType();
  if (type != 'unknown') {
    result = (type == 'Function' ? '' : '(' + type.replace(/\|/g, ', ') + '): ') + result;
  }
  return result;
}

/**
 * Extracts the entry's `example` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `example` data.
 */
function getExample() {
  var result = getMultilineValue(this.entry, 'example');
  if (result) {
    result = '```' + this.lang + '\n' + result + '\n```';
  }
  return result;
}

/**
 * Checks if the entry is an alias.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `false`.
 */
function isAlias() {
  return false;
}

/**
 * Checks if the entry is a constructor.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if a constructor, else `false`.
 */
function isCtor() {
  return hasTag(this.entry, 'constructor');
}

/**
 * Checks if the entry is a function reference.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if the entry is a function reference, else `false`.
 */
function isFunction() {
  return !!(
    this.isCtor() ||
    _.size(this.getParams()) ||
    _.size(this.getReturns()) ||
    /\*[\t ]*@function\b/.test(this.entry) ||
    /\*\/\s*function/.test(this.entry)
  );
}

/**
 * Checks if the entry is a license.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if a license, else `false`.
 */
function isLicense() {
  return hasTag(this.entry, 'license');
}

/**
 * Checks if the entry *is* assigned to a prototype.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if assigned to a prototype, else `false`.
 */
function isPlugin() {
  return (
    (!this.isCtor()) &&
    (!this.isPrivate()) &&
    (!this.isStatic())
  );
}

/**
 * Checks if the entry is private.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if private, else `false`.
 */
function isPrivate() {
  return (
    this.isLicense() ||
    hasTag(this.entry, 'private') ||
    !hasTag(this.entry, '*')
  );
}

/**
 * Checks if the entry is *not* assigned to a prototype.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if not assigned to a prototype, else `false`.
 */
function isStatic() {
  var isPublic = !this.isPrivate(),
      result = isPublic && hasTag(this.entry, 'static');

  // set in cases where it isn't explicitly stated
  if (isPublic && !result) {
    var parent = _.last((this.getMembers(0) || '').split(/[#.]/));
    if (!parent) {
      return true;
    }
    var source = this.source;
    _.each(getEntries(source), function(entry) {
      entry = new Entry(entry, source);
      if (entry.getName() == parent) {
        result = !entry.isCtor();
        return false;
      }
    });
  }
  return result;
}

/**
 * Resolves the entry's line number.
 *
 * @memberOf Entry
 * @returns {number} The entry's line number.
 */
function getLineNumber() {
  var entry = this.entry,
      lines = this.source.slice(0, this.source.indexOf(entry) + entry.length).match(/\n/g).slice(1);

  return lines.length + 1;
}

/**
 * Extracts the entry's `member` data.
 *
 * @memberOf Entry
 * @param {number} [index] The index of the array value to return.
 * @returns {Array|string} The entry's `member` data.
 */
function getMembers(index) {
  if (this._members === undefined) {
    var result = getValue(this.entry, 'member');
    if (result) {
      result = result.split(/,\s*/);
      result.sort(utils.compareNatural);
    }
    this._members = result || [];
  }
  return index != null
    ? this._members[index]
    : this._members;
}

/**
 * Extracts the entry's `name` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `name` data.
 */
function getName() {
  var result = hasTag(this.entry, 'name')
    ? getValue(this.entry, 'name')
    : _.first(this.getCall().split('('));

  return (result || '').replace(/\'/g, '');
}

/**
 * Extracts the entry's `param` data.
 *
 * @memberOf Entry
 * @param {number} [index] The index of the array value to return.
 * @returns {Array} The entry's `param` data.
 */
function getParams(index) {
  if (this._params === undefined) {
    var match,
        re = /@param\s+\{\(?([^})]+)\)?\}\s+(\[.+\]|[\w]+(?:\[.+\])?)\s+([\s\S]*?)(?=\@)/gim,
        result = [];

    while (match = re.exec(this.entry)) {
      match = match.slice(1);
      match = match.map(function(aParamPart,index) {
        return aParamPart.trim()
      });
      match[2] = match[2].replace(/(?:^|\n)[\t ]*\*[\t ]*/g, ' ');
      result.push(match);
    }
    var tuples = match ? _.compact(match) : [];
    _.each(tuples, function(tuple){
      result.push(tuple.trim());
    });

    this._params = result;
  }
  return index !== undefined
    ? this._params[index]
    : this._params;
}

/**
 * Extracts the entry's `returns` data.
 *
 * @memberOf Entry
 * @returns {array} The entry's `returns` data.
 */
function getReturns() {
  var result = getMultilineValue(this.entry, 'returns'),
      returnType = _.result(/(?:\{)([\w|\*]*)(?:\})/gi.exec(result),1);

  return returnType
    ? [returnType, result.replace(/(\{[\w|\*]*\})/gi,'')]
    : [];
}

/**
 * Extracts the entry's `type` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `type` data.
 */
function getType() {
  var result = getValue(this.entry, 'type');
  if (result) {
    if (/^(?:array|function|object|regexp)$/.test(result)) {
      result = _.capitalize(result);
    }
  } else {
    result = this.isFunction() ? 'Function' : 'unknown';
  }
  return result;
}

/**
 * Extracts the entry's hash value for permalinking.
 *
 * @memberOf Entry
 * @param {string} [style] The hash style.
 * @returns {string} The entry's hash value (without a hash itself).
 */
function getHash(style) {
  switch (style) {
    case 'github':
      return this.getCall()
        .replace(/\(\[|\[\]/g, '')
        .replace(/[\t =|\'"{}.()\]]/g, '')
        .replace(/[\[#,]+/g, '-')
        .toLowerCase();
  }
  var result = this.getMembers(0) || '';
  if (result) {
    result += '_' + (this.isPlugin() ? 'prototype_' : '');
  }
  result += this.isAlias() ? this.getOwner().getName() : this.getName();
  return result
    .replace(/\./g, '_')
    .replace(/^_+/, '');
}

/*----------------------------------------------------------------------------*/

Entry.getEntries = getEntries;

_.assign(Entry.prototype, {
  'getAliases': getAliases,
  'getCall': getCall,
  'getCategory': getCategory,
  'getDesc': getDesc,
  'getExample': getExample,
  'isAlias': isAlias,
  'isCtor': isCtor,
  'isFunction': isFunction,
  'isLicense': isLicense,
  'isPlugin': isPlugin,
  'isPrivate': isPrivate,
  'isStatic': isStatic,
  'getLineNumber': getLineNumber,
  'getMembers': getMembers,
  'getName': getName,
  'getParams': getParams,
  'getReturns': getReturns,
  'getType': getType,
  'getHash': getHash
});

module.exports = Entry;
<commit_msg>Correct github style hashes.<commit_after>'use strict';

var _ = require('lodash'),
    os = require('os'),
    Alias = require('./alias.js'),
    utils = require('./utils.js');

/*----------------------------------------------------------------------------*/

function cleanValue(string) {
  string = string == null ? '' : String(string);
  return _.trim(string.replace(/(?:^|\n)[\t ]*\*[\t ]*/g, ' '));
}

function getMultilineValue(string, tagName) {
  var prelude = tagName == 'description' ? '^ */\\*\\*(?: *\\n *\\* *)?' : ('^ *\\*[\\t ]*@' + _.escapeRegExp(tagName) + '\\b'),
      postlude = '(?=\\*\\s+\\@[a-z]|\\*/)',
      result = _.result(RegExp(prelude + '([\\s\\S]*?)' + postlude, 'gm').exec(string), 1, '');

  return _.trim(result.replace(RegExp('(?:^|\\n)[\\t ]*\\*[\\t ]' + (tagName == 'example' ? '?' : '*'), 'g'), '\n'));
}

function getValue(string, tagName) {
  tagName = tagName == 'member' ? (tagName + '(?:Of)?') : _.escapeRegExp(tagName);
  var result = _.result(RegExp('^ *\\*[\\t ]*@' + tagName + '\\s+(.+)', 'm').exec(string), 1, '');
  return cleanValue(result);
}

function hasTag(string, tagName) {
  tagName = tagName == '*' ? '\\w+' : _.escapeRegExp(tagName);
  return RegExp('^ *\\*[\\t ]*@' + tagName + '\\b', 'm').test(string);
}

/*----------------------------------------------------------------------------*/

/**
 * The Entry constructor.
 *
 * @constructor
 * @param {string} entry The documentation entry to analyse.
 * @param {string} source The source code.
 * @param {string} [lang='js'] The language highlighter used for code examples.
 */
function Entry(entry, source, lang) {
  this.entry = entry;
  this.lang = lang == null ? 'js' : lang;
  this.source = source.replace(os.EOL, '\n');
  this.getCall = _.memoize(getCall);
  this.getCategory = _.memoize(getCategory);
  this.getDesc = _.memoize(getDesc);
  this.getExample = _.memoize(getExample);
  this.isAlias = _.memoize(isAlias);
  this.isCtor = _.memoize(isCtor);
  this.isFunction = _.memoize(isFunction);
  this.isLicense = _.memoize(isLicense);
  this.isPlugin = _.memoize(isPlugin);
  this.isPrivate = _.memoize(isPrivate);
  this.isStatic = _.memoize(isStatic);
  this.getLineNumber = _.memoize(getLineNumber);
  this.getName = _.memoize(getName);
  this.getReturns = _.memoize(getReturns);
  this.getType = _.memoize(getType);
  this._aliases = this._members = this._params = undefined;
}

/**
 * Extracts the documentation entries from source code.
 *
 * @static
 * @memberOf Entry
 * @param {string} source The source code.
 * @returns {Array} The array of entries.
 */
function getEntries(source) {
  source = source == null ? '' : String(source);
  return source.match(/\/\*\*(?![-!])[\s\S]*?\*\/\s*.+/g) || [];
}

/**
 * Extracts the entry's `alias` objects.
 *
 * @memberOf Entry
 * @param {number} index The index of the array value to return.
 * @returns {Array|string} The entry's `alias` objects.
 */
function getAliases(index) {
  if (this._aliases === undefined) {
    var result = _.result(/\*[\t ]*@alias\s+(.+)/.exec(this.entry), 1);
    if (result) {
      result = result.replace(/(?:^|\n)[\t ]*\*[\t ]*/, ' ').trim();
      result = result.split(/,\s*/);
      result.sort(utils.compareNatural);

      result = _.map(result, function(value) {
        return new Alias(value, this);
      }, this);
    }
    this._aliases = result;
  }
  return index != null
    ? this._aliases[index]
    : this._aliases;
}

/**
 * Extracts the function call from the entry.
 *
 * @memberOf Entry
 * @returns {string} The function call.
 */
function getCall() {
  var result = /\*\/\s*(?:function ([^(]*)|(.*?)(?=[:=,]|return\b))/.exec(this.entry);
  if (result) {
    result = (result[1] || result[2]).split('.').pop();
    result = _.trim(_.trim(result), "'").split('var ').pop();
    result = _.trim(result);
  }
  // resolve name
  // avoid this.getName() because it calls this.getCall()
  var name = _.result(/\*[\t ]*@name\s+(.+)/.exec(this.entry), 1, result || '');
  if (!this.isFunction()) {
    return name;
  }
  var params = this.getParams(),
      paramNames = [];

  // compile function call syntax
  result = [result];
  _.each(params, function(param) {
    // skip params that are properties of other params (e.g. `options.leading`)
    var parentParam = _.result(/\w+(?=\.[\w.]+)/.exec(param[1]), 0);
    if (!_.contains(paramNames, parentParam)) {
      result.push(param[1]);
    }
    paramNames.push(param[1].replace(/^\[|\]/, ''));
  });

  // format
  return name + '(' + result.slice(1).join(', ') + ')';
}

/**
 * Extracts the entry's `category` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `category` data.
 */
function getCategory() {
  var result = _.result(/\*[\t ]*@category\s+(.+)/.exec(this.entry), 1, '');
  return result
    ? cleanValue(result)
    : (this.getType() == 'Function' ? 'Methods' : 'Properties');
}

/**
 * Extracts the entry's description.
 *
 * @memberOf Entry
 * @returns {string} The entry's description.
 */
function getDesc() {
  var result = getMultilineValue(this.entry, 'description');
  if (!result) {
    return result;
  }
  result = _.trim(result
    .replace(/:\n[\t ]*\*[\t ]*/g, ':
\n')
    .replace(/(?:^|\n)[\t ]*\*\n[\t ]*\*[\t ]*/g, '\n\n')
    .replace(/(?:^|\n)[\t ]*\*[\t ]?/g, ' ')
  );

  var type = this.getType();
  if (type != 'unknown') {
    result = (type == 'Function' ? '' : '(' + type.replace(/\|/g, ', ') + '): ') + result;
  }
  return result;
}

/**
 * Extracts the entry's `example` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `example` data.
 */
function getExample() {
  var result = getMultilineValue(this.entry, 'example');
  if (result) {
    result = '```' + this.lang + '\n' + result + '\n```';
  }
  return result;
}

/**
 * Checks if the entry is an alias.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `false`.
 */
function isAlias() {
  return false;
}

/**
 * Checks if the entry is a constructor.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if a constructor, else `false`.
 */
function isCtor() {
  return hasTag(this.entry, 'constructor');
}

/**
 * Checks if the entry is a function reference.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if the entry is a function reference, else `false`.
 */
function isFunction() {
  return !!(
    this.isCtor() ||
    _.size(this.getParams()) ||
    _.size(this.getReturns()) ||
    /\*[\t ]*@function\b/.test(this.entry) ||
    /\*\/\s*function/.test(this.entry)
  );
}

/**
 * Checks if the entry is a license.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if a license, else `false`.
 */
function isLicense() {
  return hasTag(this.entry, 'license');
}

/**
 * Checks if the entry *is* assigned to a prototype.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if assigned to a prototype, else `false`.
 */
function isPlugin() {
  return (
    (!this.isCtor()) &&
    (!this.isPrivate()) &&
    (!this.isStatic())
  );
}

/**
 * Checks if the entry is private.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if private, else `false`.
 */
function isPrivate() {
  return (
    this.isLicense() ||
    hasTag(this.entry, 'private') ||
    !hasTag(this.entry, '*')
  );
}

/**
 * Checks if the entry is *not* assigned to a prototype.
 *
 * @memberOf Entry
 * @returns {boolean} Returns `true` if not assigned to a prototype, else `false`.
 */
function isStatic() {
  var isPublic = !this.isPrivate(),
      result = isPublic && hasTag(this.entry, 'static');

  // set in cases where it isn't explicitly stated
  if (isPublic && !result) {
    var parent = _.last((this.getMembers(0) || '').split(/[#.]/));
    if (!parent) {
      return true;
    }
    var source = this.source;
    _.each(getEntries(source), function(entry) {
      entry = new Entry(entry, source);
      if (entry.getName() == parent) {
        result = !entry.isCtor();
        return false;
      }
    });
  }
  return result;
}

/**
 * Resolves the entry's line number.
 *
 * @memberOf Entry
 * @returns {number} The entry's line number.
 */
function getLineNumber() {
  var entry = this.entry,
      lines = this.source.slice(0, this.source.indexOf(entry) + entry.length).match(/\n/g).slice(1);

  return lines.length + 1;
}

/**
 * Extracts the entry's `member` data.
 *
 * @memberOf Entry
 * @param {number} [index] The index of the array value to return.
 * @returns {Array|string} The entry's `member` data.
 */
function getMembers(index) {
  if (this._members === undefined) {
    var result = getValue(this.entry, 'member');
    if (result) {
      result = result.split(/,\s*/);
      result.sort(utils.compareNatural);
    }
    this._members = result || [];
  }
  return index != null
    ? this._members[index]
    : this._members;
}

/**
 * Extracts the entry's `name` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `name` data.
 */
function getName() {
  var result = hasTag(this.entry, 'name')
    ? getValue(this.entry, 'name')
    : _.first(this.getCall().split('('));

  return (result || '').replace(/\'/g, '');
}

/**
 * Extracts the entry's `param` data.
 *
 * @memberOf Entry
 * @param {number} [index] The index of the array value to return.
 * @returns {Array} The entry's `param` data.
 */
function getParams(index) {
  if (this._params === undefined) {
    var match,
        re = /@param\s+\{\(?([^})]+)\)?\}\s+(\[.+\]|[\w]+(?:\[.+\])?)\s+([\s\S]*?)(?=\@)/gim,
        result = [];

    while (match = re.exec(this.entry)) {
      match = match.slice(1);
      match = match.map(function(aParamPart,index) {
        return aParamPart.trim()
      });
      match[2] = match[2].replace(/(?:^|\n)[\t ]*\*[\t ]*/g, ' ');
      result.push(match);
    }
    var tuples = match ? _.compact(match) : [];
    _.each(tuples, function(tuple){
      result.push(tuple.trim());
    });

    this._params = result;
  }
  return index !== undefined
    ? this._params[index]
    : this._params;
}

/**
 * Extracts the entry's `returns` data.
 *
 * @memberOf Entry
 * @returns {array} The entry's `returns` data.
 */
function getReturns() {
  var result = getMultilineValue(this.entry, 'returns'),
      returnType = _.result(/(?:\{)([\w|\*]*)(?:\})/gi.exec(result),1);

  return returnType
    ? [returnType, result.replace(/(\{[\w|\*]*\})/gi,'')]
    : [];
}

/**
 * Extracts the entry's `type` data.
 *
 * @memberOf Entry
 * @returns {string} The entry's `type` data.
 */
function getType() {
  var result = getValue(this.entry, 'type');
  if (result) {
    if (/^(?:array|function|object|regexp)$/.test(result)) {
      result = _.capitalize(result);
    }
  } else {
    result = this.isFunction() ? 'Function' : 'unknown';
  }
  return result;
}

/**
 * Extracts the entry's hash value for permalinking.
 *
 * @memberOf Entry
 * @param {string} [style] The hash style.
 * @returns {string} The entry's hash value (without a hash itself).
 */
function getHash(style) {
  var result = this.getMembers(0) || '';

  switch (style) {
    case 'github':
      if (result) {
        result += this.isPlugin() ? 'prototype' : '';
      }
      result += this.getCall();
      return result
        .replace(/\(\[|\[\]/g, '')
        .replace(/[\t =|\'"{}.()\]]/g, '')
        .replace(/[\[#,]+/g, '-')
        .toLowerCase();

    case 'default':
    default:
      if (result) {
        result += '_' + (this.isPlugin() ? 'prototype_' : '');
      }
      result += this.isAlias() ? this.getOwner().getName() : this.getName();
      return result
        .replace(/\./g, '_')
        .replace(/^_+/, '');
  }
}

/*----------------------------------------------------------------------------*/

Entry.getEntries = getEntries;

_.assign(Entry.prototype, {
  'getAliases': getAliases,
  'getCall': getCall,
  'getCategory': getCategory,
  'getDesc': getDesc,
  'getExample': getExample,
  'isAlias': isAlias,
  'isCtor': isCtor,
  'isFunction': isFunction,
  'isLicense': isLicense,
  'isPlugin': isPlugin,
  'isPrivate': isPrivate,
  'isStatic': isStatic,
  'getLineNumber': getLineNumber,
  'getMembers': getMembers,
  'getName': getName,
  'getParams': getParams,
  'getReturns': getReturns,
  'getType': getType,
  'getHash': getHash
});

module.exports = Entry;
<|endoftext|>


<commit_before>import React from "react";
import { getTypeLink } from "./getTypeLink";
import { intersperse } from "../../../../../src/array/intersperse";

/**
 * @param {DoctrineType} type
 * @param {number?} i
 * @returns {*}
 */
export function getParameterType(type, i) {
  let value;
  if (type.type === "AllLiteral") {
    value = "any";
  } else if (type.type === "ArrayType") {
    value = getArrayType(type);
  } else if (type.type === "RecordType") {
    value = getRecordType(type);
  } else if (type.type === "TypeApplication") {
    value = getTypeApplication(type);
  } else if (type.type === "FieldType") {
    value = getTypeLink(type.key);
  } else if (type.type) {
    value = getTypeLink(type.name);
  } else {
    value = "void";
  }

  if (value.props && i != null) {
    value = React.cloneElement(value, { key: i });
  }

  return value;
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getArrayType(type) {
  const open = "[ ";
  const elements = type.elements.map(getParameterType);
  const close = " ]";
  return {open}{intersperse(elements, ", ")}{close};
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getRecordType(type) {
  const open = "{ ";
  const elements = type.fields.map(getParameterType);
  const close = " }";
  return {open}{intersperse(elements, ", ")}{close};
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getTypeApplication(type) {
  const main = getTypeLink(type.expression.name);
  const open = "<";
  const elements = type.applications.map(getParameterType);
  const close = ">";
  return {main}{open}{intersperse(elements, ", ")}{close};
}
<commit_msg>Refactors to more appropriate variable names.<commit_after>import React from "react";
import { getTypeLink } from "./getTypeLink";
import { intersperse } from "../../../../../src/array/intersperse";

/**
 * @param {DoctrineType} type
 * @param {number?} i
 * @returns {*}
 */
export function getParameterType(type, i) {
  let value;
  if (type.type === "AllLiteral") {
    value = "any";
  } else if (type.type === "ArrayType") {
    value = getArrayType(type);
  } else if (type.type === "RecordType") {
    value = getRecordType(type);
  } else if (type.type === "TypeApplication") {
    value = getTypeApplication(type);
  } else if (type.type === "FieldType") {
    value = getTypeLink(type.key);
  } else if (type.type) {
    value = getTypeLink(type.name);
  } else {
    value = "void";
  }

  if (value.props && i != null) {
    value = React.cloneElement(value, { key: i });
  }

  return value;
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getArrayType(type) {
  const open = "[ ";
  const elements = type.elements.map(getParameterType);
  const close = " ]";
  return {open}{intersperse(elements, ", ")}{close};
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getRecordType(type) {
  const open = "{ ";
  const fields = type.fields.map(getParameterType);
  const close = " }";
  return {open}{intersperse(fields, ", ")}{close};
}

/**
 * @param {DoctrineType} type
 * @returns {XML}
 */
function getTypeApplication(type) {
  const main = getTypeLink(type.expression.name);
  const open = "<";
  const applications = type.applications.map(getParameterType);
  const close = ">";
  return {main}{open}{intersperse(applications, ", ")}{close};
}
<|endoftext|>