<commit_before>'use strict';

const util = require('util');

const stringifyEntity = require('bem-naming').stringify;

/**
 * Enum for types of BEM entities.
 *
 * @readonly
 * @enum {String}
 */
const TYPES = {
    BLOCK:     'block',
    BLOCK_MOD: 'blockMod',
    ELEM:      'elem',
    ELEM_MOD:  'elemMod'
};

module.exports = class BemEntityName {
    /**
     * @param {object} obj — representation of entity name.
     * @param {string} obj.block  — the block name of entity.
     * @param {string} [obj.elem] — the element name of entity.
     * @param {object} [obj.mod]  — the modifier of entity.
     * @param {string} [obj.mod.name] — the modifier name of entity.
     * @param {string} [obj.mod.val]  — the modifier value of entity.
     */
    constructor(obj) {
        if (!obj.block) {
             throw new Error('This is not valid BEM entity: the field `block` is undefined.');
        }

        const data = this._data = { block: obj.block };

        obj.elem && (data.elem = obj.elem);

        const modObj = obj.mod;
        const modName = (typeof modObj === 'string' ? modObj : modObj && modObj.name) || obj.modName;
        const hasModVal = modObj && modObj.hasOwnProperty('val') || obj.hasOwnProperty('modVal');

        if (modName) {
            data.mod = {
                name: modName,
                val: hasModVal ? modObj && modObj.val || obj.modVal : true
            };
        } else if (modObj || hasModVal) {
            throw new Error('This is not valid BEM entity: the field `mod.name` is undefined.');
        }
    }

    /**
     * Returns the name of block to which this entity belongs.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * name.block; // button
     *
     * @returns {string} name of entity block.
     */
    get block() { return this._data.block; }

    /**
     * Returns the element name of this entity.
     *
     * If entity is not element or modifier of element then returns empty string.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', elem: 'text' });
     *
     * name.elem; // text
     *
     * @returns {string} name of entity element.
     */
    get elem() { return this._data.elem; }

    /**
     * Returns the modifier of this entity.
     *
     * Important: If entity is not a modifier then returns `undefined`.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     *
     * const blockName = new BemEntityName({ block: 'button' });
     * const modName = new BemEntityName({ block: 'button', mod: 'disabled' });
     *
     * modName.mod;   // { name: 'disabled', val: true }
     * blockName.mod; // undefined
     *
     * @returns {{mod: string, val: *}} entity modifier.
     */
    get mod() { return this._data.mod; }

    /**
     * Returns the modifier name of this entity.
     *
     * If entity is not modifier then returns `undefined`.
     *
     * @returns {string} entity modifier name.
     * @deprecated use `mod.name` instead.
     */
    get modName() { return this.mod && this.mod.name; }

    /**
     * Returns the modifier value of this entity.
     *
     * If entity is not modifier then returns `undefined`.
     *
     * @returns {string} entity modifier name.
     * @deprecated use `mod.val` instead.
     */
    get modVal() { return this.mod && this.mod.val; }

    /**
     * Returns id for this entity.
     *
     * Important: should only be used to determine uniqueness of entity.
     *
     * If you want to get string representation in accordance with the provisions naming convention
     * you should use `bem-naming` package.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'disabled' });
     *
     * name.id; // button_disabled
     *
     * @returns {string} id of entity.
     */
    get id() {
        if (this._id) { return this._id; }

        const entity = { block: this._data.block };

        this.elem && (entity.elem = this.elem);
        this.modName && (entity.modName = this.modName);
        this.modVal && (entity.modVal = this.modVal);

        this._id = stringifyEntity(entity);

        return this._id;
    }

    /**
     * Returns type for this entity.
     *
     * @example
type of element

     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', elem: 'text' });
     *
     * name.type; // elem
     *
     * @example
type of element modifier

     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'menu', elem: 'item', mod: 'current' });
     *
     * name.type; // elemMod
     *
     * @returns {string} type of entity.
     */
    get type() {
        if (this._type) { return this._type; }

        const data = this._data;
        const isMod = data.mod;

        this._type = data.elem
            ? isMod ? TYPES.ELEM_MOD : TYPES.ELEM
            : isMod ? TYPES.BLOCK_MOD : TYPES.BLOCK;

        return this._type;
    }
    /**
     * Returns string representing the entity name.
     *
     * Important: If you want to get string representation in accordance with the provisions naming convention
     * you should use `bem-naming` package.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'focused' });
     *
     * name.toString(); // button_focused
     *
     * @returns {string}
     */
    toString() { return this.id; }

    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In some browsers `console.log()` calls `valueOf()` on each argument.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'focused' });
     *
     * name.valueOf();
     *
     * // ➜ { block: 'button', mod: { name: 'focused', value: true } }
     *
     * @returns {{block: string, elem: ?string, mod: ?{name: ?string, val: *}}}
     */
    valueOf() { return this._data; }

    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In Node.js, `console.log()` calls `util.inspect()` on each argument without a formatting placeholder.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(name); // BemEntityName { block: 'button' }
     *
     * @param {integer} depth — tells inspect how many times to recurse while formatting the object.
     * @param {object} options — An optional `options` object may be passed
     *                         	 that alters certain aspects of the formatted string.
     *
     * @returns {string}
     */
    inspect(depth, options) {
        const stringRepresentation = util.inspect(this._data, options);

        return `BemEntityName ${stringRepresentation}`;
    }

    /**
     * Determines whether specified entity is the deepEqual entity.
     *
     * @param {BemEntityName} entityName - the entity to compare.
     *
     * @returns {boolean} A Boolean indicating whether or not specified entity is the deepEqual entity.
     * @example
     * const BemEntityName = require('@bem/entity-name');
     *
     * const inputName = new BemEntityName({ block: 'input' });
     * const buttonName = new BemEntityName({ block: 'button' });
     *
     * inputName.isEqual(buttonName); // false
     * buttonName.isEqual(buttonName); // true
     */
    isEqual(entityName) {
        return entityName && (this.id === entityName.id);
    }
};
<commit_msg>feat(isBemEntityName): add `isBemEntityName` method<commit_after>'use strict';

const util = require('util');

const stringifyEntity = require('bem-naming').stringify;

/**
 * Enum for types of BEM entities.
 *
 * @readonly
 * @enum {String}
 */
const TYPES = {
    BLOCK:     'block',
    BLOCK_MOD: 'blockMod',
    ELEM:      'elem',
    ELEM_MOD:  'elemMod'
};

module.exports = class BemEntityName {
    /**
     * @param {object} obj — representation of entity name.
     * @param {string} obj.block  — the block name of entity.
     * @param {string} [obj.elem] — the element name of entity.
     * @param {object} [obj.mod]  — the modifier of entity.
     * @param {string} [obj.mod.name] — the modifier name of entity.
     * @param {string} [obj.mod.val]  — the modifier value of entity.
     */
    constructor(obj) {
        if (!obj.block) {
             throw new Error('This is not valid BEM entity: the field `block` is undefined.');
        }

        const data = this._data = { block: obj.block };

        obj.elem && (data.elem = obj.elem);

        const modObj = obj.mod;
        const modName = (typeof modObj === 'string' ? modObj : modObj && modObj.name) || obj.modName;
        const hasModVal = modObj && modObj.hasOwnProperty('val') || obj.hasOwnProperty('modVal');

        if (modName) {
            data.mod = {
                name: modName,
                val: hasModVal ? modObj && modObj.val || obj.modVal : true
            };
        } else if (modObj || hasModVal) {
            throw new Error('This is not valid BEM entity: the field `mod.name` is undefined.');
        }

        this.__isBemEntityName__ = true;
    }

    /**
     * Returns the name of block to which this entity belongs.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * name.block; // button
     *
     * @returns {string} name of entity block.
     */
    get block() { return this._data.block; }

    /**
     * Returns the element name of this entity.
     *
     * If entity is not element or modifier of element then returns empty string.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', elem: 'text' });
     *
     * name.elem; // text
     *
     * @returns {string} name of entity element.
     */
    get elem() { return this._data.elem; }

    /**
     * Returns the modifier of this entity.
     *
     * Important: If entity is not a modifier then returns `undefined`.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     *
     * const blockName = new BemEntityName({ block: 'button' });
     * const modName = new BemEntityName({ block: 'button', mod: 'disabled' });
     *
     * modName.mod;   // { name: 'disabled', val: true }
     * blockName.mod; // undefined
     *
     * @returns {{mod: string, val: *}} entity modifier.
     */
    get mod() { return this._data.mod; }

    /**
     * Returns the modifier name of this entity.
     *
     * If entity is not modifier then returns `undefined`.
     *
     * @returns {string} entity modifier name.
     * @deprecated use `mod.name` instead.
     */
    get modName() { return this.mod && this.mod.name; }

    /**
     * Returns the modifier value of this entity.
     *
     * If entity is not modifier then returns `undefined`.
     *
     * @returns {string} entity modifier name.
     * @deprecated use `mod.val` instead.
     */
    get modVal() { return this.mod && this.mod.val; }

    /**
     * Returns id for this entity.
     *
     * Important: should only be used to determine uniqueness of entity.
     *
     * If you want to get string representation in accordance with the provisions naming convention
     * you should use `bem-naming` package.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'disabled' });
     *
     * name.id; // button_disabled
     *
     * @returns {string} id of entity.
     */
    get id() {
        if (this._id) { return this._id; }

        const entity = { block: this._data.block };

        this.elem && (entity.elem = this.elem);
        this.modName && (entity.modName = this.modName);
        this.modVal && (entity.modVal = this.modVal);

        this._id = stringifyEntity(entity);

        return this._id;
    }

    /**
     * Returns type for this entity.
     *
     * @example
type of element

     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', elem: 'text' });
     *
     * name.type; // elem
     *
     * @example
type of element modifier

     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'menu', elem: 'item', mod: 'current' });
     *
     * name.type; // elemMod
     *
     * @returns {string} type of entity.
     */
    get type() {
        if (this._type) { return this._type; }

        const data = this._data;
        const isMod = data.mod;

        this._type = data.elem
            ? isMod ? TYPES.ELEM_MOD : TYPES.ELEM
            : isMod ? TYPES.BLOCK_MOD : TYPES.BLOCK;

        return this._type;
    }
    /**
     * Returns string representing the entity name.
     *
     * Important: If you want to get string representation in accordance with the provisions naming convention
     * you should use `bem-naming` package.
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'focused' });
     *
     * name.toString(); // button_focused
     *
     * @returns {string}
     */
    toString() { return this.id; }

    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In some browsers `console.log()` calls `valueOf()` on each argument.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button', mod: 'focused' });
     *
     * name.valueOf();
     *
     * // ➜ { block: 'button', mod: { name: 'focused', value: true } }
     *
     * @returns {{block: string, elem: ?string, mod: ?{name: ?string, val: *}}}
     */
    valueOf() { return this._data; }

    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In Node.js, `console.log()` calls `util.inspect()` on each argument without a formatting placeholder.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @example
     * const BemEntityName = require('@bem/entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(name); // BemEntityName { block: 'button' }
     *
     * @param {integer} depth — tells inspect how many times to recurse while formatting the object.
     * @param {object} options — An optional `options` object may be passed
     *                         	 that alters certain aspects of the formatted string.
     *
     * @returns {string}
     */
    inspect(depth, options) {
        const stringRepresentation = util.inspect(this._data, options);

        return `BemEntityName ${stringRepresentation}`;
    }

    /**
     * Determines whether specified entity is the deepEqual entity.
     *
     * @param {BemEntityName} entityName - the entity to compare.
     *
     * @returns {boolean} A Boolean indicating whether or not specified entity is the deepEqual entity.
     * @example
     * const BemEntityName = require('@bem/entity-name');
     *
     * const inputName = new BemEntityName({ block: 'input' });
     * const buttonName = new BemEntityName({ block: 'button' });
     *
     * inputName.isEqual(buttonName); // false
     * buttonName.isEqual(buttonName); // true
     */
    isEqual(entityName) {
        return entityName && (this.id === entityName.id);
    }

    /**
     * Determines whether specified entity is instance of BemEntityName.
     *
     * @param {BemEntityName} entityName - the entity to check.
     *
     * @returns {boolean} A Boolean indicating whether or not specified entity is instance of BemEntityName.
     * @example
     * const BemEntityName = require('@bem/entity-name');
     *
     * const entityName = new BemEntityName({ block: 'input' });
     *
     * BemEntityName.isBemEntityName(entityName); // true
     * BemEntityName.isBemEntityName({}); // false
     */
    static isBemEntityName(entityName) {
        return entityName && entityName.__isBemEntityName__;
    }
};
<|endoftext|>


<commit_before>var Factory = require("factory-girl");
var Adapter = Factory.Adapter;

var MongooseAdapter = function(){};

MongooseAdapter.prototype.build = function(Model, attributes) {
  return new Model(attributes);
};

MongooseAdapter.prototype.get = function(doc, attr, Model) {
  return doc.get(attr);
};

MongooseAdapter.prototype.set = function(props, doc, Model) {
  return doc.set(props);
};

MongooseAdapter.prototype.save = function(doc, Model, callback) {

  doc.save(function (err) {

    if (err) {
      return callback(err);
    }
    callback();
  });
};

MongooseAdapter.prototype.destroy = function(doc, Model, callback) {

  doc.remove(function (err) {

    if (err) {
      return callback(err);
    }
    callback();
  });
};

exports.MongooseAdapter = new MongooseAdapter();
<commit_msg>Removed unneeded requires<commit_after>
var MongooseAdapter = function () {};

MongooseAdapter.prototype.build = function(Model, attributes) {
  return new Model(attributes);
};

MongooseAdapter.prototype.get = function(doc, attr, Model) {
  return doc.get(attr);
};

MongooseAdapter.prototype.set = function(props, doc, Model) {
  return doc.set(props);
};

MongooseAdapter.prototype.save = function(doc, Model, callback) {
  doc.save(function (err) {
    if (err) {
      return callback(err);
    }
    callback();
  });
};

MongooseAdapter.prototype.destroy = function(doc, Model, callback) {

  doc.remove(function (err) {

    if (err) {
      return callback(err);
    }
    callback();
  });
};

exports.MongooseAdapter = new MongooseAdapter();
<|endoftext|>


<commit_before>exports.Client = Client
exports.Server = Server

var bncode = require('bncode')
var compact2string = require('compact2string')
var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
var hat = require('hat')
var http = require('http')
var inherits = require('inherits')
var querystring = require('querystring')
var string2compact = require('string2compact')
var dgram = require('dgram')
var parseUrl = require('url').parse

var CONNECTION_ID = Buffer.concat([ toUInt32(0x417), toUInt32(0x27101980) ])
var ACTIONS = { CONNECT: 0, ANNOUNCE: 1 }
var EVENTS = { completed: 1, started: 2, stopped: 3 }

inherits(Client, EventEmitter)

function Client (peerId, port, torrent, opts) {
  var self = this
  if (!(self instanceof Client)) return new Client(peerId, port, torrent, opts)
  EventEmitter.call(self)
  self._opts = opts || {}

  // required
  self._peerId = Buffer.isBuffer(peerId)
    ? peerId
    : new Buffer(torrent.peerId, 'utf8')
  self._port = port
  self._infoHash = Buffer.isBuffer(torrent.infoHash)
    ? torrent.infoHash
    : new Buffer(torrent.infoHash, 'hex')
  self._torrentLength = torrent.length
  self._announce = torrent.announce

  // optional
  self._numWant = self._opts.numWant || 80
  self._intervalMs = self._opts.interval || (30 * 60 * 1000) // default: 30 minutes

  self._interval = null
}

Client.prototype.start = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'started'
  self._request(opts)

  self.setInterval(self._intervalMs) // start announcing on intervals
}

Client.prototype.stop = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'stopped'
  self._request(opts)

  self.setInterval(0) // stop announcing on intervals
}

Client.prototype.complete = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'completed'
  opts.downloaded = self._torrentLength
  self._request(opts)
}

Client.prototype.update = function (opts) {
  var self = this
  opts = opts || {}
  self._request(opts)
}

Client.prototype.setInterval = function (intervalMs) {
  var self = this
  if (self._interval) {
    clearInterval(self._interval)
  }

  self._intervalMs = intervalMs
  if (self._intervalMs) {
    self._interval = setInterval(self.update.bind(self), self._intervalMs)
  }
}

/**
 * Send a request to the tracker
 */
Client.prototype._request = function (opts) {
  var self = this
  opts = extend({
    info_hash: bytewiseEncodeURIComponent(self._infoHash),
    peer_id: bytewiseEncodeURIComponent(self._peerId),
    port: self._port,
    left: self._torrentLength - (opts.downloaded || 0),
    compact: 1,
    numwant: self._numWant,
    uploaded: 0, // default, user should provide real value
    downloaded: 0 // default, user should provide real value
  }, opts)

  if (self._trackerId) {
    opts.trackerid = self._trackerId
  }

  self._announce.forEach(function (announceUrl) {
    if (announceUrl.indexOf('udp:') === 0) {
      self._requestUdp(announceUrl, opts)
    } else {
      self._requestHttp(announceUrl, opts)
    }
  })
}

Client.prototype._requestHttp = function (announceUrl, opts) {
  var self = this
  var url = announceUrl + '?' + querystring.stringify(opts)

  var req = http.get(url, function (res) {
    var data = ''
    if (res.statusCode !== 200) {
      res.resume() // consume the whole stream
      self.emit('error', new Error('Invalid response code ' + res.statusCode + ' from tracker'))
      return
    }
    res.on('data', function (chunk) {
      data += chunk
    })
    res.on('end', function () {
      self._handleResponse(data, announceUrl)
    })
  })

  req.on('error', function (err) {
    self.emit('error', err)
  })
}

Client.prototype._requestUdp = function (announceUrl, opts) {
  var self = this
  var parsedUrl = parseUrl(announceUrl)
  var socket = dgram.createSocket('udp4')
  var transactionId = new Buffer(hat(32), 'hex')

  var timeout = setTimeout(function () {
    error('tracker request timed out')
  }, 15000)

  if (timeout.unref) {
    timeout.unref()
  }

  function error (message) {
    self.emit('error', new Error(message))
    socket.close()
    clearTimeout(timeout)
  }

  socket.on('error', function (err) {
    error(err)
  })

  socket.on('message', function (message, rinfo) {

    if (message.length < 8 || message.readUInt32BE(4) !== transactionId.readUInt32BE(0)) {
      return error(new Error('tracker sent back invalid transaction id'))
    }

    var action = message.readUInt32BE(0)
    switch (action) {
      case 0:
        if (message.length < 16) {
          return error(new Error('invalid udp handshake'))
        }
        announce(message.slice(8, 16), opts)
        return

      case 1:
        if (message.length < 20) {
          return error(new Error('invalid announce message'))
        }

        // TODO: this should be stored per tracker, not globally for all trackers
        var interval = message.readUInt32BE(8)
        if (interval && !self._opts.interval && self._intervalMs !== 0) {
          // use the interval the tracker recommends, UNLESS the user manually specifies an
          // interval they want to use
          self.setInterval(interval * 1000)
        }

        self.emit('update', {
          announce: announceUrl,
          complete: message.readUInt32BE(16),
          incomplete: message.readUInt32BE(12)
        })

        compact2string.multi(message.slice(20)).forEach(function (addr) {
          self.emit('peer', addr)
        })

        clearTimeout(timeout)
        socket.close()
    }
  })

  function send (message) {
    if (!parsedUrl.port) {
      parsedUrl.port = 80;
    }
    socket.send(message, 0, message.length, parsedUrl.port, parsedUrl.hostname)
  }

  function announce (connectionId, opts) {
    opts = opts || {}
    transactionId = new Buffer(hat(32), 'hex')

    send(Buffer.concat([
      connectionId,
      toUInt32(ACTIONS.ANNOUNCE),
      transactionId,
      self._infoHash,
      self._peerId,
      toUInt32(0), toUInt32(opts.downloaded || 0), // 64bit
      toUInt32(0), toUInt32(opts.left || 0), // 64bit
      toUInt32(0), toUInt32(opts.uploaded || 0), // 64bit
      toUInt32(EVENTS[opts.event] || 0),
      toUInt32(0), // ip address (optional)
      toUInt32(0), // key (optional)
      toUInt32(self._numWant),
      toUInt16(self._port || 0)
    ]))
  }

  send(Buffer.concat([
    CONNECTION_ID,
    toUInt32(ACTIONS.CONNECT),
    transactionId
  ]))
}

Client.prototype._handleResponse = function (data, announceUrl) {
  var self = this

  try {
    data = bncode.decode(data)
  } catch (err) {
    return self.emit('error', new Error('Error decoding tracker response: ' + err.message))
  }
  var failure = data['failure reason']
  if (failure) {
    return self.emit('error', new Error(failure))
  }

  var warning = data['warning message']
  if (warning) {
    self.emit('warning', warning);
  }

  // TODO: this should be stored per tracker, not globally for all trackers
  var interval = data.interval || data['min interval']
  if (interval && !self._opts.interval && self._intervalMs !== 0) {
    // use the interval the tracker recommends, UNLESS the user manually specifies an
    // interval they want to use
    self.setInterval(interval * 1000)
  }

  // TODO: this should be stored per tracker, not globally for all trackers
  var trackerId = data['tracker id']
  if (trackerId) {
    // If absent, do not discard previous trackerId value
    self._trackerId = trackerId
  }

  self.emit('update', {
    announce: announceUrl,
    complete: data.complete,
    incomplete: data.incomplete
  })

  if (Buffer.isBuffer(data.peers)) {
    // tracker returned compact response
    compact2string.multi(data.peers).forEach(function (addr) {
      self.emit('peer', addr)
    })
  } else if (Array.isArray(data.peers)) {
    // tracker returned normal response
    data.peers.forEach(function (peer) {
      var ip = peer.ip
      self.emit('peer', ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + ip[3] + ':' + peer.port)
    })
  }
}

inherits(Server, EventEmitter)

function Server (opts) {
  var self = this
  if (!(self instanceof Server)) return new Server(opts)
  EventEmitter.call(self)
  opts = opts || {}

  self._interval = opts.interval
    ? opts.interval / 1000
    : 10 * 60 // 10 min (in secs)

  self._trustProxy = !!opts.trustProxy

  self.torrents = {}

  self._server = http.createServer()
  self._server.on('request', self._onRequest.bind(self))
  self._server.on('error', function (err) {
    self.emit('error', err)
  })
}

Server.prototype.listen = function (port) {
  var self = this
  self._server.listen(port, function () {
    self.emit('listening')
  })
}

Server.prototype.close = function (cb) {
  var self = this
  self._server.close(cb)
}

Server.prototype._onRequest = function (req, res) {
  var self = this

  function error (message) {
    res.end(bncode.encode({
      'failure reason': message
    }))
    self.emit('error', new Error(message))
  }

  var warning
  var s = req.url.split('?')

  if (s[0] === '/announce') {
    var params = querystring.parse(s[1])

    var ip = self._trustProxy
      ? req.headers['x-forwarded-for'] || req.connection.remoteAddress
      : req.connection.remoteAddress
    var port = Number(params.port)
    var addr = ip + ':' + port

    var infoHash = bytewiseDecodeURIComponent(params.info_hash)
    var peerId = bytewiseDecodeURIComponent(params.peer_id)

    var swarm = self.torrents[infoHash]
    if (!swarm) {
      swarm = self.torrents[infoHash] = {
        complete: 0,
        incomplete: 0,
        peers: {}
      }
    }
    var peer = swarm.peers[addr]
    switch (params.event) {
      case 'started':
        if (peer) {
          warning = 'unexpected `started` event from peer that is already in swarm'
        }

        var left = Number(params.left)

        if (left === 0) {
          swarm.complete += 1
        } else {
          swarm.incomplete += 1
        }

        peer = swarm.peers[addr] = {
          ip: ip,
          port: port,
          peerId: peerId
        }
        self.emit('start', addr, params)
        break

      case 'stopped':
        if (!peer) {
          return error('unexpected `stopped` event from peer that is not in swarm')
        }

        if (peer.complete) {
          swarm.complete -= 1
        } else {
          swarm.incomplete -= 1
        }

        delete swarm.peers[addr]

        self.emit('stop', addr, params)
        break

      case 'completed':
        if (!peer) {
          return error('unexpected `completed` event from peer that is not in swarm')
        }
        if (peer.complete) {
          warning = 'unexpected `completed` event from peer that is already marked as completed'
        }
        peer.complete = true

        swarm.complete += 1
        swarm.incomplete -= 1

        self.emit('complete', addr, params)
        break

      case '': // update
      case undefined:
        if (!peer) {
          return error('unexpected `update` event from peer that is not in swarm')
        }

        self.emit('update', addr, params)
        break

      default:
        return error('unexpected event: ' + params.event) // early return
    }

    // send peers
    var peers = Number(params.compact) === 1
      ? self._getPeersCompact(swarm)
      : self._getPeers(swarm)

    var response = {
      complete: swarm.complete,
      incomplete: swarm.incomplete,
      peers: peers,
      interval: self._interval
    }

    if (warning) {
      response['warning message'] = warning
    }

    res.end(bncode.encode(response))
  }
}

Server.prototype._getPeers = function (swarm) {
  var self = this
  var peers = []
  for (var peerId in swarm.peers) {
    var peer = swarm.peers[peerId]
    peers.push({
      'peer id': peer.peerId,
      ip: peer.ip,
      port: peer.port
    })
  }
  return peers
}

Server.prototype._getPeersCompact = function (swarm) {
  var self = this
  var addrs = Object.keys(swarm.peers).map(function (peerId) {
    var peer = swarm.peers[peerId]
    return peer.ip + ':' + peer.port
  })
  return string2compact(addrs)
}

//
// HELPERS
//

function toUInt16 (n) {
  var buf = new Buffer(2)
  buf.writeUInt16BE(n, 0)
  return buf
}

function toUInt32 (n) {
  var buf = new Buffer(4)
  buf.writeUInt32BE(n, 0)
  return buf
}

function bytewiseEncodeURIComponent (buf) {
  return encodeURIComponent(buf.toString('binary'))
}

function bytewiseDecodeURIComponent (str) {
  return (new Buffer(decodeURIComponent(str), 'binary').toString('hex'))
}
<commit_msg>fix the fix<commit_after>exports.Client = Client
exports.Server = Server

var bncode = require('bncode')
var compact2string = require('compact2string')
var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
var hat = require('hat')
var http = require('http')
var inherits = require('inherits')
var querystring = require('querystring')
var string2compact = require('string2compact')
var dgram = require('dgram')
var parseUrl = require('url').parse

var CONNECTION_ID = Buffer.concat([ toUInt32(0x417), toUInt32(0x27101980) ])
var ACTIONS = { CONNECT: 0, ANNOUNCE: 1 }
var EVENTS = { completed: 1, started: 2, stopped: 3 }

inherits(Client, EventEmitter)

function Client (peerId, port, torrent, opts) {
  var self = this
  if (!(self instanceof Client)) return new Client(peerId, port, torrent, opts)
  EventEmitter.call(self)
  self._opts = opts || {}

  // required
  self._peerId = Buffer.isBuffer(peerId)
    ? peerId
    : new Buffer(peerId, 'utf8')
  self._port = port
  self._infoHash = Buffer.isBuffer(torrent.infoHash)
    ? torrent.infoHash
    : new Buffer(torrent.infoHash, 'hex')
  self._torrentLength = torrent.length
  self._announce = torrent.announce

  // optional
  self._numWant = self._opts.numWant || 80
  self._intervalMs = self._opts.interval || (30 * 60 * 1000) // default: 30 minutes

  self._interval = null
}

Client.prototype.start = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'started'
  self._request(opts)

  self.setInterval(self._intervalMs) // start announcing on intervals
}

Client.prototype.stop = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'stopped'
  self._request(opts)

  self.setInterval(0) // stop announcing on intervals
}

Client.prototype.complete = function (opts) {
  var self = this
  opts = opts || {}
  opts.event = 'completed'
  opts.downloaded = self._torrentLength
  self._request(opts)
}

Client.prototype.update = function (opts) {
  var self = this
  opts = opts || {}
  self._request(opts)
}

Client.prototype.setInterval = function (intervalMs) {
  var self = this
  if (self._interval) {
    clearInterval(self._interval)
  }

  self._intervalMs = intervalMs
  if (self._intervalMs) {
    self._interval = setInterval(self.update.bind(self), self._intervalMs)
  }
}

/**
 * Send a request to the tracker
 */
Client.prototype._request = function (opts) {
  var self = this
  opts = extend({
    info_hash: bytewiseEncodeURIComponent(self._infoHash),
    peer_id: bytewiseEncodeURIComponent(self._peerId),
    port: self._port,
    left: self._torrentLength - (opts.downloaded || 0),
    compact: 1,
    numwant: self._numWant,
    uploaded: 0, // default, user should provide real value
    downloaded: 0 // default, user should provide real value
  }, opts)

  if (self._trackerId) {
    opts.trackerid = self._trackerId
  }

  self._announce.forEach(function (announceUrl) {
    if (announceUrl.indexOf('udp:') === 0) {
      self._requestUdp(announceUrl, opts)
    } else {
      self._requestHttp(announceUrl, opts)
    }
  })
}

Client.prototype._requestHttp = function (announceUrl, opts) {
  var self = this
  var url = announceUrl + '?' + querystring.stringify(opts)

  var req = http.get(url, function (res) {
    var data = ''
    if (res.statusCode !== 200) {
      res.resume() // consume the whole stream
      self.emit('error', new Error('Invalid response code ' + res.statusCode + ' from tracker'))
      return
    }
    res.on('data', function (chunk) {
      data += chunk
    })
    res.on('end', function () {
      self._handleResponse(data, announceUrl)
    })
  })

  req.on('error', function (err) {
    self.emit('error', err)
  })
}

Client.prototype._requestUdp = function (announceUrl, opts) {
  var self = this
  var parsedUrl = parseUrl(announceUrl)
  var socket = dgram.createSocket('udp4')
  var transactionId = new Buffer(hat(32), 'hex')

  var timeout = setTimeout(function () {
    error('tracker request timed out')
  }, 15000)

  if (timeout.unref) {
    timeout.unref()
  }

  function error (message) {
    self.emit('error', new Error(message))
    socket.close()
    clearTimeout(timeout)
  }

  socket.on('error', function (err) {
    error(err)
  })

  socket.on('message', function (message, rinfo) {

    if (message.length < 8 || message.readUInt32BE(4) !== transactionId.readUInt32BE(0)) {
      return error(new Error('tracker sent back invalid transaction id'))
    }

    var action = message.readUInt32BE(0)
    switch (action) {
      case 0:
        if (message.length < 16) {
          return error(new Error('invalid udp handshake'))
        }
        announce(message.slice(8, 16), opts)
        return

      case 1:
        if (message.length < 20) {
          return error(new Error('invalid announce message'))
        }

        // TODO: this should be stored per tracker, not globally for all trackers
        var interval = message.readUInt32BE(8)
        if (interval && !self._opts.interval && self._intervalMs !== 0) {
          // use the interval the tracker recommends, UNLESS the user manually specifies an
          // interval they want to use
          self.setInterval(interval * 1000)
        }

        self.emit('update', {
          announce: announceUrl,
          complete: message.readUInt32BE(16),
          incomplete: message.readUInt32BE(12)
        })

        compact2string.multi(message.slice(20)).forEach(function (addr) {
          self.emit('peer', addr)
        })

        clearTimeout(timeout)
        socket.close()
    }
  })

  function send (message) {
    if (!parsedUrl.port) {
      parsedUrl.port = 80;
    }
    socket.send(message, 0, message.length, parsedUrl.port, parsedUrl.hostname)
  }

  function announce (connectionId, opts) {
    opts = opts || {}
    transactionId = new Buffer(hat(32), 'hex')

    send(Buffer.concat([
      connectionId,
      toUInt32(ACTIONS.ANNOUNCE),
      transactionId,
      self._infoHash,
      self._peerId,
      toUInt32(0), toUInt32(opts.downloaded || 0), // 64bit
      toUInt32(0), toUInt32(opts.left || 0), // 64bit
      toUInt32(0), toUInt32(opts.uploaded || 0), // 64bit
      toUInt32(EVENTS[opts.event] || 0),
      toUInt32(0), // ip address (optional)
      toUInt32(0), // key (optional)
      toUInt32(self._numWant),
      toUInt16(self._port || 0)
    ]))
  }

  send(Buffer.concat([
    CONNECTION_ID,
    toUInt32(ACTIONS.CONNECT),
    transactionId
  ]))
}

Client.prototype._handleResponse = function (data, announceUrl) {
  var self = this

  try {
    data = bncode.decode(data)
  } catch (err) {
    return self.emit('error', new Error('Error decoding tracker response: ' + err.message))
  }
  var failure = data['failure reason']
  if (failure) {
    return self.emit('error', new Error(failure))
  }

  var warning = data['warning message']
  if (warning) {
    self.emit('warning', warning);
  }

  // TODO: this should be stored per tracker, not globally for all trackers
  var interval = data.interval || data['min interval']
  if (interval && !self._opts.interval && self._intervalMs !== 0) {
    // use the interval the tracker recommends, UNLESS the user manually specifies an
    // interval they want to use
    self.setInterval(interval * 1000)
  }

  // TODO: this should be stored per tracker, not globally for all trackers
  var trackerId = data['tracker id']
  if (trackerId) {
    // If absent, do not discard previous trackerId value
    self._trackerId = trackerId
  }

  self.emit('update', {
    announce: announceUrl,
    complete: data.complete,
    incomplete: data.incomplete
  })

  if (Buffer.isBuffer(data.peers)) {
    // tracker returned compact response
    compact2string.multi(data.peers).forEach(function (addr) {
      self.emit('peer', addr)
    })
  } else if (Array.isArray(data.peers)) {
    // tracker returned normal response
    data.peers.forEach(function (peer) {
      var ip = peer.ip
      self.emit('peer', ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + ip[3] + ':' + peer.port)
    })
  }
}

inherits(Server, EventEmitter)

function Server (opts) {
  var self = this
  if (!(self instanceof Server)) return new Server(opts)
  EventEmitter.call(self)
  opts = opts || {}

  self._interval = opts.interval
    ? opts.interval / 1000
    : 10 * 60 // 10 min (in secs)

  self._trustProxy = !!opts.trustProxy

  self.torrents = {}

  self._server = http.createServer()
  self._server.on('request', self._onRequest.bind(self))
  self._server.on('error', function (err) {
    self.emit('error', err)
  })
}

Server.prototype.listen = function (port) {
  var self = this
  self._server.listen(port, function () {
    self.emit('listening')
  })
}

Server.prototype.close = function (cb) {
  var self = this
  self._server.close(cb)
}

Server.prototype._onRequest = function (req, res) {
  var self = this

  function error (message) {
    res.end(bncode.encode({
      'failure reason': message
    }))
    self.emit('error', new Error(message))
  }

  var warning
  var s = req.url.split('?')

  if (s[0] === '/announce') {
    var params = querystring.parse(s[1])

    var ip = self._trustProxy
      ? req.headers['x-forwarded-for'] || req.connection.remoteAddress
      : req.connection.remoteAddress
    var port = Number(params.port)
    var addr = ip + ':' + port

    var infoHash = bytewiseDecodeURIComponent(params.info_hash)
    var peerId = bytewiseDecodeURIComponent(params.peer_id)

    var swarm = self.torrents[infoHash]
    if (!swarm) {
      swarm = self.torrents[infoHash] = {
        complete: 0,
        incomplete: 0,
        peers: {}
      }
    }
    var peer = swarm.peers[addr]
    switch (params.event) {
      case 'started':
        if (peer) {
          warning = 'unexpected `started` event from peer that is already in swarm'
        }

        var left = Number(params.left)

        if (left === 0) {
          swarm.complete += 1
        } else {
          swarm.incomplete += 1
        }

        peer = swarm.peers[addr] = {
          ip: ip,
          port: port,
          peerId: peerId
        }
        self.emit('start', addr, params)
        break

      case 'stopped':
        if (!peer) {
          return error('unexpected `stopped` event from peer that is not in swarm')
        }

        if (peer.complete) {
          swarm.complete -= 1
        } else {
          swarm.incomplete -= 1
        }

        delete swarm.peers[addr]

        self.emit('stop', addr, params)
        break

      case 'completed':
        if (!peer) {
          return error('unexpected `completed` event from peer that is not in swarm')
        }
        if (peer.complete) {
          warning = 'unexpected `completed` event from peer that is already marked as completed'
        }
        peer.complete = true

        swarm.complete += 1
        swarm.incomplete -= 1

        self.emit('complete', addr, params)
        break

      case '': // update
      case undefined:
        if (!peer) {
          return error('unexpected `update` event from peer that is not in swarm')
        }

        self.emit('update', addr, params)
        break

      default:
        return error('unexpected event: ' + params.event) // early return
    }

    // send peers
    var peers = Number(params.compact) === 1
      ? self._getPeersCompact(swarm)
      : self._getPeers(swarm)

    var response = {
      complete: swarm.complete,
      incomplete: swarm.incomplete,
      peers: peers,
      interval: self._interval
    }

    if (warning) {
      response['warning message'] = warning
    }

    res.end(bncode.encode(response))
  }
}

Server.prototype._getPeers = function (swarm) {
  var self = this
  var peers = []
  for (var peerId in swarm.peers) {
    var peer = swarm.peers[peerId]
    peers.push({
      'peer id': peer.peerId,
      ip: peer.ip,
      port: peer.port
    })
  }
  return peers
}

Server.prototype._getPeersCompact = function (swarm) {
  var self = this
  var addrs = Object.keys(swarm.peers).map(function (peerId) {
    var peer = swarm.peers[peerId]
    return peer.ip + ':' + peer.port
  })
  return string2compact(addrs)
}

//
// HELPERS
//

function toUInt16 (n) {
  var buf = new Buffer(2)
  buf.writeUInt16BE(n, 0)
  return buf
}

function toUInt32 (n) {
  var buf = new Buffer(4)
  buf.writeUInt32BE(n, 0)
  return buf
}

function bytewiseEncodeURIComponent (buf) {
  return encodeURIComponent(buf.toString('binary'))
}

function bytewiseDecodeURIComponent (str) {
  return (new Buffer(decodeURIComponent(str), 'binary').toString('hex'))
}
<|endoftext|>


<commit_before>var loaded_blocks = {};
var cdn = "https://cdn.jsdelivr.net/gh/googlefonts/";
function fontname2url(font) {
  var fontpath = window.fontfiles[font];
  fontpath = fontpath.replace("noto-fonts", "noto-fonts@main");
  fontpath = fontpath.replace("noto-cjk", "noto-cjk@main");
  fontpath = fontpath.replace("noto-emoji", "noto-emoji@main");
  return cdn + fontpath;
}

function doModal(block) {
  var myModal = new bootstrap.Modal(document.getElementById("modal"), {});
  $("#modal-title").html(block.name);
  appendFullBlock(block);
  myModal.handleUpdate();
  myModal.show();
}

function loadBlock() {
  var ix = $(this).data("block-id");
  padded = ix.toString().padStart(3, 0);
  filename = `blocks/block-${padded}.json`;
  if (ix in loaded_blocks) {
    doModal(loaded_blocks[ix]);
  } else {
    $.getJSON(filename, function (block) {
      loaded_blocks[ix] = block;
      doModal(loaded_blocks[ix]);
    });
  }
}

function toHex(d) {
  return d.toString(16).toUpperCase().padStart(4, 0);
}

function appendSummaryBlock(ix, mydata) {
  width = 200;
  height = 200;

  var mydiv = $('
');
  mydiv.data("block-id", mydata.ix);
  mydiv.click(loadBlock);
  $("#panes").append(mydiv);
  if (coveragepercent(mydata) == 1.0) {
    mydiv.addClass("covered");
  } else if (coveragepercent(mydata) == 0.0) {
    mydiv.addClass("empty");
  } else {
    mydiv.addClass("partial");
  }

  var title = $('
');
  title.html(`<h5>${mydata.name}</h5>`);
  mydiv.append(title);

  var range = $('
');
  range.html(
    `U+${toHex(mydata.start)}-U+${toHex(
      mydata.end
    )}`
  );
  mydiv.append(range);

  var visualization = $('
');
  mydiv.append(visualization);

  var svg = d3
    .select(visualization[0])
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g");

  var codepoints = mydata.end - mydata.start + 1;
  rows = parseInt(codepoints ** 0.5);
  cols = Math.ceil(codepoints / rows);
  rows_domain = [...Array(rows).keys()];
  cols_domain = [...Array(cols).keys()];
  var summary = mydata.summary.split("");
  data = [];
  var index = 0;
  for (var thing of summary) {
    data.push({
      row: parseInt(index / cols),
      col: index % cols,
      cp: index + mydata.start,
      status: thing,
    });
    index = index + 1;
  }

  myColor = function (status) {
    if (status == "X") {
      return "silver";
    }
    if (status == "1") {
      return "olivedrab";
    }
    if (status == "M") {
      return "darkgreen";
    }
    if (status == "0") {
      return "crimson";
    }
  };
  // Build X scales and axis:
  x = d3.scaleBand().range([0, width]).domain(cols_domain).padding(0.05);

  // Build Y scales and axis:
  y = d3.scaleBand().range([0, height]).domain(rows_domain).padding(0.05);

  // add the squares
  svg
    .selectAll()
    .data(data, function (d) {
      return d.cp;
    })
    .enter()
    .append("rect")
    .attr("x", function (d) {
      return x(d.col);
    })
    .attr("y", function (d) {
      return y(d.row);
    })
    .attr("rx", 4)
    .attr("ry", 4)
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function (d) {
      return d3.color(myColor(d.status));
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8);
}

function appendFullBlock(mydata) {
  width = 500;
  height = 500;

  var visualization = $("#modal-body");
  $(visualization).empty();
  var svg = d3
    .select(visualization[0])
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g");

  var codepoints = mydata.end - mydata.start + 1;
  rows = parseInt(codepoints ** 0.5);
  cols = Math.ceil(codepoints / rows);
  rows_domain = [...Array(rows).keys()];
  cols_domain = [...Array(cols).keys()];
  data = [];
  var index = 0;
  // console.log(mydata);
  while (index < codepoints) {
    var ix = mydata.start + index;
    var cp = mydata.cps[ix] || { unassigned: "true" };
    if ("fonts" in mydata) {
      fonts = mydata.fonts;
    } else if ("fonts" in cp) {
      fonts = cp.fonts;
    } else {
      fonts = "";
    }
    data.push({
      row: parseInt(index / cols),
      col: index % cols,
      cp: index + mydata.start,
      name: cp.name,
      special: cp.special,
      unassigned: cp.unassigned,
      fonts: fonts,
      value: cp,
      age: mydata.age || cp.age,
    });
    index = index + 1;
  }
  // console.log(data);

  myColor = function (el) {
    if (el.unassigned) {
      return "silver";
    }
    if (el.special) {
      return "black";
    }
    if (el.fonts.length == 1) {
      return "olivedrab";
    }
    if (el.fonts.length > 1) {
      return "darkgreen";
    }
    if (el.fonts.length == 0) {
      return "crimson";
    }
  };

  // create a tooltip
  var tooltip = d3
    .select(visualization[0])
    .append("div")
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px");

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function (d) {
    tooltip.style("opacity", 1);
    if (d.unassigned) {
      return;
    }
    d3.select(this).style("stroke", "black").style("opacity", 1);
  };
  var mousemove = function (d) {
    if (d.unassigned) {
      return;
    }
    tooltip
      .style("left", d3.mouse(this)[0] + 70 + "px")
      .style("top", d3.mouse(this)[1] + 70 + "px");
    if (d.cp == window.oldcp) {
      return;
    }
    window.oldcp = d.cp;
    var start_string;
    if (!d.fonts[0]) {
      start_string = "";
    }
    if (window.curfont != d.fonts[0]) {
      start_string = "Loading...";
    } else {
      start_string = String.fromCodePoint(d.cp);
    }
    tooltip.html(
      `

${start_string}

Codepoint

${toHex(
            d.cp
          )}

Name

${d.name}

Age

${d.age}

Fonts

${(
            d.fonts || []
          ).join(", ")}

      `
    );

    if (d.fonts && window.curfont != d.fonts[0]) {
      setTimeout(function () {
        var thisfont = d.fonts[0];
        // console.log("Loading");
        $("#anotofont").text(`
            @font-face {
               font-family: "Noto ${thisfont}";
               src: url(${fontname2url(thisfont)});
             }
            .noto { font-family: "Noto ${thisfont}", sans-serif; }
          `);
        fontSpy(`Noto ${thisfont}`, {
          glyphs: String.fromCodePoint(d.cp),
          success: function () {
            $("#char").html(String.fromCodePoint(d.cp));
            // console.log("Loaded font " + thisfont);
          },
          failure: function () {
            // console.log("Failed to load font " + thisfont);
            $("#char").html(String.fromCodePoint(d.cp));
          },
        });
        window.curfont = d.fonts[0];
      }, 0);
    }
  };
  var mouseleave = function (d) {
    if (d.unassigned) {
      return;
    }
    tooltip.style("opacity", 0);
    d3.select(this).style("stroke", "none").style("opacity", 0.8);
  };

  // Build X scales and axis:
  x = d3.scaleBand().range([0, width]).domain(cols_domain).padding(0.05);

  // Build Y scales and axis:
  y = d3.scaleBand().range([0, height]).domain(rows_domain).padding(0.05);

  // add the squares
  svg
    .selectAll()
    .data(data, function (d) {
      return d.cp;
    })
    .enter()
    .append("rect")
    .attr("x", function (d) {
      return x(d.col);
    })
    .attr("y", function (d) {
      return y(d.row);
    })
    .attr("rx", 4)
    .attr("ry", 4)
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function (d) {
      return d3.color(myColor(d));
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8)
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseleave", mouseleave);
}

function rebuildSummary() {
  $("#panes").empty();
  for (ix in window.summary) {
    appendSummaryBlock(ix, window.summary[ix]);
  }
}

function coveragepercent(summary_block) {
  var len = summary_block.summary.length;
  var missing = (summary_block.summary.match(/0/g) || []).length;
  var there = (summary_block.summary.match(/[1M]/g) || []).length;
  if (!(missing + there)) {
    return 0;
  }
  return there / (missing + there);
}

$(function () {
  $.getJSON("fontfiles.json", function (data) {
    window.fontfiles = data;
    window.curfont = "";
    window.oldcp = 0;
    $.getJSON("blocks.json", function (data) {
      window.summary = data;
      rebuildSummary();
    });
  });
  $("#sort").change(function () {
    if ($("#sort").val() == "alpha") {
      window.summary = window.summary.sort((a, b) =>
        a.name.localeCompare(b.name)
      );
    } else if ($("#sort").val() == "coverage") {
      window.summary = window.summary.sort(
        (a, b) => coveragepercent(a) - coveragepercent(b)
      );
    } else {
      window.summary = window.summary.sort((a, b) => a.ix - b.ix);
    }
    rebuildSummary();
  });
});
<commit_msg>Hide unassigned<commit_after>var loaded_blocks = {};
var cdn = "https://cdn.jsdelivr.net/gh/googlefonts/";
function fontname2url(font) {
  var fontpath = window.fontfiles[font];
  fontpath = fontpath.replace("noto-fonts", "noto-fonts@main");
  fontpath = fontpath.replace("noto-cjk", "noto-cjk@main");
  fontpath = fontpath.replace("noto-emoji", "noto-emoji@main");
  return cdn + fontpath;
}

function doModal(block) {
  var myModal = new bootstrap.Modal(document.getElementById("modal"), {});
  $("#modal-title").html(block.name);
  appendFullBlock(block);
  myModal.handleUpdate();
  myModal.show();
}

function loadBlock() {
  var ix = $(this).data("block-id");
  padded = ix.toString().padStart(3, 0);
  filename = `blocks/block-${padded}.json`;
  if (ix in loaded_blocks) {
    doModal(loaded_blocks[ix]);
  } else {
    $.getJSON(filename, function (block) {
      loaded_blocks[ix] = block;
      doModal(loaded_blocks[ix]);
    });
  }
}

function toHex(d) {
  return d.toString(16).toUpperCase().padStart(4, 0);
}

function appendSummaryBlock(ix, mydata) {
  width = 200;
  height = 200;

  var mydiv = $('
');
  mydiv.data("block-id", mydata.ix);
  mydiv.click(loadBlock);
  $("#panes").append(mydiv);
  if (coveragepercent(mydata) == 1.0) {
    mydiv.addClass("covered");
  } else if (coveragepercent(mydata) == 0.0) {
    mydiv.addClass("empty");
  } else {
    mydiv.addClass("partial");
  }

  var title = $('
');
  title.html(`<h5>${mydata.name}</h5>`);
  mydiv.append(title);

  var range = $('
');
  range.html(
    `U+${toHex(mydata.start)}-U+${toHex(
      mydata.end
    )}`
  );
  mydiv.append(range);

  var visualization = $('
');
  mydiv.append(visualization);

  var svg = d3
    .select(visualization[0])
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g");

  var codepoints = mydata.end - mydata.start + 1;
  rows = parseInt(codepoints ** 0.5);
  cols = Math.ceil(codepoints / rows);
  rows_domain = [...Array(rows).keys()];
  cols_domain = [...Array(cols).keys()];
  var summary = mydata.summary.split("");
  data = [];
  var index = 0;
  for (var thing of summary) {
    data.push({
      row: parseInt(index / cols),
      col: index % cols,
      cp: index + mydata.start,
      status: thing,
    });
    index = index + 1;
  }

  myColor = function (status) {
    if (status == "X") {
      return "silver";
    }
    if (status == "1") {
      return "olivedrab";
    }
    if (status == "M") {
      return "darkgreen";
    }
    if (status == "0") {
      return "crimson";
    }
  };
  // Build X scales and axis:
  x = d3.scaleBand().range([0, width]).domain(cols_domain).padding(0.05);

  // Build Y scales and axis:
  y = d3.scaleBand().range([0, height]).domain(rows_domain).padding(0.05);

  // add the squares
  svg
    .selectAll()
    .data(data, function (d) {
      return d.cp;
    })
    .enter()
    .append("rect")
    .attr("x", function (d) {
      return x(d.col);
    })
    .attr("y", function (d) {
      return y(d.row);
    })
    .attr("rx", 4)
    .attr("ry", 4)
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function (d) {
      return d3.color(myColor(d.status));
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8);
}

function appendFullBlock(mydata) {
  width = 500;
  height = 500;

  var visualization = $("#modal-body");
  $(visualization).empty();
  var svg = d3
    .select(visualization[0])
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g");

  var codepoints = mydata.end - mydata.start + 1;
  rows = parseInt(codepoints ** 0.5);
  cols = Math.ceil(codepoints / rows);
  rows_domain = [...Array(rows).keys()];
  cols_domain = [...Array(cols).keys()];
  data = [];
  var index = 0;
  // console.log(mydata);
  while (index < codepoints) {
    var ix = mydata.start + index;
    var cp = mydata.cps[ix] || { unassigned: "true" };
    if ("fonts" in mydata) {
      fonts = mydata.fonts;
    } else if ("fonts" in cp) {
      fonts = cp.fonts;
    } else {
      fonts = "";
    }
    data.push({
      row: parseInt(index / cols),
      col: index % cols,
      cp: index + mydata.start,
      name: cp.name,
      special: cp.special,
      unassigned: cp.unassigned,
      fonts: fonts,
      value: cp,
      age: mydata.age || cp.age,
    });
    index = index + 1;
  }
  // console.log(data);

  myColor = function (el) {
    if (el.unassigned) {
      return "silver";
    }
    if (el.special) {
      return "black";
    }
    if (el.fonts.length == 1) {
      return "olivedrab";
    }
    if (el.fonts.length > 1) {
      return "darkgreen";
    }
    if (el.fonts.length == 0) {
      return "crimson";
    }
  };

  // create a tooltip
  var tooltip = d3
    .select(visualization[0])
    .append("div")
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px");

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function (d) {
    if (d.unassigned) {
      return;
    }
    tooltip.style("opacity", 1);
    d3.select(this).style("stroke", "black").style("opacity", 1);
  };
  var mousemove = function (d) {
    if (d.unassigned) {
      return;
    }
    tooltip
      .style("left", d3.mouse(this)[0] + 70 + "px")
      .style("top", d3.mouse(this)[1] + 70 + "px");
    if (d.cp == window.oldcp) {
      return;
    }
    window.oldcp = d.cp;
    var start_string;
    if (!d.fonts[0]) {
      start_string = "";
    }
    if (window.curfont != d.fonts[0]) {
      start_string = "Loading...";
    } else {
      start_string = String.fromCodePoint(d.cp);
    }
    tooltip.html(
      `

${start_string}

Codepoint

${toHex(
            d.cp
          )}

Name

${d.name}

Age

${d.age}

Fonts

${(
            d.fonts || []
          ).join(", ")}

      `
    );

    if (d.fonts && window.curfont != d.fonts[0]) {
      setTimeout(function () {
        var thisfont = d.fonts[0];
        // console.log("Loading");
        $("#anotofont").text(`
            @font-face {
               font-family: "Noto ${thisfont}";
               src: url(${fontname2url(thisfont)});
             }
            .noto { font-family: "Noto ${thisfont}", sans-serif; }
          `);
        fontSpy(`Noto ${thisfont}`, {
          glyphs: String.fromCodePoint(d.cp),
          success: function () {
            $("#char").html(String.fromCodePoint(d.cp));
            // console.log("Loaded font " + thisfont);
          },
          failure: function () {
            // console.log("Failed to load font " + thisfont);
            $("#char").html(String.fromCodePoint(d.cp));
          },
        });
        window.curfont = d.fonts[0];
      }, 0);
    }
  };
  var mouseleave = function (d) {
    tooltip.style("opacity", 0);
    d3.select(this).style("stroke", "none").style("opacity", 0.8);
  };

  // Build X scales and axis:
  x = d3.scaleBand().range([0, width]).domain(cols_domain).padding(0.05);

  // Build Y scales and axis:
  y = d3.scaleBand().range([0, height]).domain(rows_domain).padding(0.05);

  // add the squares
  svg
    .selectAll()
    .data(data, function (d) {
      return d.cp;
    })
    .enter()
    .append("rect")
    .attr("x", function (d) {
      return x(d.col);
    })
    .attr("y", function (d) {
      return y(d.row);
    })
    .attr("rx", 4)
    .attr("ry", 4)
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function (d) {
      return d3.color(myColor(d));
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8)
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseleave", mouseleave);
}

function rebuildSummary() {
  $("#panes").empty();
  for (ix in window.summary) {
    appendSummaryBlock(ix, window.summary[ix]);
  }
}

function coveragepercent(summary_block) {
  var len = summary_block.summary.length;
  var missing = (summary_block.summary.match(/0/g) || []).length;
  var there = (summary_block.summary.match(/[1M]/g) || []).length;
  if (!(missing + there)) {
    return 0;
  }
  return there / (missing + there);
}

$(function () {
  $.getJSON("fontfiles.json", function (data) {
    window.fontfiles = data;
    window.curfont = "";
    window.oldcp = 0;
    $.getJSON("blocks.json", function (data) {
      window.summary = data;
      rebuildSummary();
    });
  });
  $("#sort").change(function () {
    if ($("#sort").val() == "alpha") {
      window.summary = window.summary.sort((a, b) =>
        a.name.localeCompare(b.name)
      );
    } else if ($("#sort").val() == "coverage") {
      window.summary = window.summary.sort(
        (a, b) => coveragepercent(a) - coveragepercent(b)
      );
    } else {
      window.summary = window.summary.sort((a, b) => a.ix - b.ix);
    }
    rebuildSummary();
  });
});
<|endoftext|>


<commit_before>'use strict';
var Ractive = require('ractive');
var htmlExtReg = /\.html$/i;
var path = require('path');
var fs = require('fs');
var zipObject = require('lodash-node/modern/arrays/zipObject');
var env = process.env.NODE_ENV || 'development';
var glob = require('glob');
var rewrite = require('rev-rewriter');
var assign = require('lodash-node/modern/objects/assign');

exports.getPartials = getPartials;
exports.cRevPost = cRevPost;
exports.rewriteComponentSource = rewriteComponentSource;

function cRevPost(component) {
    component = component || '';
    return function (assetFilePath) {
        var prefix = component ? {
            '.js': '/js/',
            '.css': '/css/'
        }[path.extname(assetFilePath)] || '/img/' : '/assets/';
        return component + prefix + assetFilePath;
    };
}

function rewriteComponentSource(filePath, source) {
    var index, component;
    if ((index = filePath.indexOf('/ccc/')) > -1) {
        component = filePath.match(/\/ccc\/[^\/]+/)[0];
        source = rewrite({
            assetPathPrefix: '/js/',
            revPost: cRevPost(component)
        }, source);
        source = rewrite({
            assetPathPrefix: '/css/',
            revPost: cRevPost(component)
        }, source);
        source = rewrite({
            assetPathPrefix: '/img/',
            revPost: cRevPost(component)
        }, source);
        return source;
    }
    return source;
}

function getPartials(appRoot, absoluteViewPath) { //TODO: production 优化，cache
    var partialsRoot = path.join(appRoot, 'partials');
    if (!fs.existsSync(partialsRoot)) {
        return addComponentPartials({});
    }
    var partialPairs = fs.readdirSync(partialsRoot)
        .filter(function (filename) {
            return filename.match(htmlExtReg) &&
                fs.statSync(path.join(partialsRoot, filename))
                .isFile();
        })
        .map(function (filename) {
            var filePath = path.join(partialsRoot, filename);
            var template = rewriteComponentSource(filePath, fs.readFileSync(
                filePath, 'utf-8'));

            return [
                filename.replace(htmlExtReg, ''),
                template
            ]; //TODO: production 优化，save parsed template
        });
    return addComponentPartials(zipObject(partialPairs));

    function addComponentPartials(globalPartials) {
        var index, componentRoot;
        if (absoluteViewPath &&
            (index = absoluteViewPath.indexOf('/ccc/')) > -1) {
            if ((index = absoluteViewPath.indexOf('/views/')) > -1) {
                componentRoot = absoluteViewPath.substring(0, index);
                return assign(globalPartials, getPartials(componentRoot));
            }
        } else {
            return globalPartials;
        }
    }
}

exports.engine = function (filePath, options, fn) {
    try {
        var template = fs.readFileSync(filePath, 'utf-8');
        template = rewrite({
            revPost: cRevPost('')
        }, template);
        template = rewriteComponentSource(filePath, template);
        var html = new Ractive({
            partials: options.partials,
            template: template, //TODO: production 优化，cache
            data: options
        })
            .toHTML();
        var appRoot = options.appRoot;
        if (appRoot && options.assetsDirName) {
            var libs = JSON.parse(fs.readFileSync(path.join(appRoot,
                options.assetsDirName,
                'js',
                'lib.json'), 'utf-8'))
                .map(function (lib) {
                    return path.resolve(path.join(appRoot,
                        'assets', 'js'), lib)
                        .substring(appRoot.length);
                });
            var libJsReplaced;
            html = html.replace(
                /(<script\s+src=["']?)\/assets\/js\/lib.js(["']?><\/script>)/g,
                function (all, p1, p2) {
                    return libJsReplaced ? '' : p1 + libs.join(p2 + p1) +
                        p2;
                });
        }
        fn(null, html);
    } catch (err) {
        fn(err);
    }
};

exports.middleware = function (opts) {
    var resolvedViewPath = {}; //TODO: refactory
    return function (req, res, next) {
        var reqPath = req.path.replace(/\/$/, '');
        if (res.viewPath) {
            return render(path.join(opts.appRoot, opts.viewsDirName, res.viewPath));
        }
        var viewPath = path.join(opts.appRoot, opts.viewsDirName, reqPath);
        if (env === 'production' && viewPath in
            resolvedViewPath) {
            var rvp = resolvedViewPath[viewPath];
            if (false === rvp) {
                return notFound();
            } else {
                return render(rvp);
            }
        }
        findViewAndRender('./views' + reqPath + '.html',
            function () {
                findViewAndRender('./views' + reqPath +
                    '/index.html', function () {
                        findViewAndRender(
                            './ccc/*/views' + reqPath +
                            '.html',
                            function () {
                                findViewAndRender(
                                    './ccc/*/views' +
                                    reqPath + '/index.html',
                                    notFound);
                            });
                    });
            });

        function notFound() { //TODO: fix it
            resolvedViewPath[viewPath] = false;
            next();
        }

        function findViewAndRender(viewPath, nf) {
            glob(viewPath, {
                cwd: opts.appRoot
            }, function (error, files) {
                if (!files.length) {
                    return nf();
                }
                var absoluteViewPath = path.join(opts.appRoot,
                    files[0]);

                res.locals.partials = getPartials(opts.appRoot,
                    absoluteViewPath);

                render(absoluteViewPath);
            });
        }

        function render(rvp) {
            if (env === 'production' && viewPath in
                resolvedViewPath) {
                resolvedViewPath[viewPath] = rvp;
            }
            res.render(rvp);
        }
    };
};

exports.argmentApp = function (app, opts) {
    app.set('view engine', 'html');
    app.engine('html', exports.engine);
    app.locals.appRoot = opts.appRoot;
    app.locals.assetsDirName = opts.assetsDirName;
    app.use(function (req, res, next) {
        var _render = res.render;
        res.render = function (view, options, fn) {
            if ('function' === typeof options) {
                fn = options;
                options = {};
            }
            this.locals.partials = this.locals.partials || getPartials(opts
                .appRoot);
            options = assign({}, app.locals, this.locals, options);
            this.locals = {};
            _render.call(this, view, options, fn);
        };
        next();
    });
    app.use(exports.middleware(opts));
};<commit_msg>add nested partails support<commit_after>'use strict';
var Ractive = require('ractive');
var htmlExtReg = /\.html$/i;
var path = require('path');
var fs = require('fs');
var zipObject = require('lodash-node/modern/arrays/zipObject');
var env = process.env.NODE_ENV || 'development';
var glob = require('glob');
var rewrite = require('rev-rewriter');
var assign = require('lodash-node/modern/objects/assign');

exports.getPartials = getPartials;
exports.cRevPost = cRevPost;
exports.rewriteComponentSource = rewriteComponentSource;

function cRevPost(component) {
    component = component || '';
    return function (assetFilePath) {
        var prefix = component ? {
            '.js': '/js/',
            '.css': '/css/'
        }[path.extname(assetFilePath)] || '/img/' : '/assets/';
        return component + prefix + assetFilePath;
    };
}

function rewriteComponentSource(filePath, source) {
    var index, component;
    if ((index = filePath.indexOf('/ccc/')) > -1) {
        component = filePath.match(/\/ccc\/[^\/]+/)[0];
        source = rewrite({
            assetPathPrefix: '/js/',
            revPost: cRevPost(component)
        }, source);
        source = rewrite({
            assetPathPrefix: '/css/',
            revPost: cRevPost(component)
        }, source);
        source = rewrite({
            assetPathPrefix: '/img/',
            revPost: cRevPost(component)
        }, source);
        return source;
    }
    return source;
}

function getPartials(appRoot, absoluteViewPath) { //TODO: production 优化，cache
    var partialsRoot = path.join(appRoot, 'partials');
    if (!fs.existsSync(partialsRoot)) {
        return addComponentPartials({});
    }

    // support nested partials
    // such as
    //     /ccc/account/partials/settings/bankcard.html
    //                                   /agreement.html
    // 引用 {{>settings.bankcard}}
    var obj = getPartialsObj(partialsRoot); // global partials
    return addComponentPartials(obj);

    function addComponentPartials(globalPartials) {
        var index, componentRoot;
        if (absoluteViewPath &&
            (index = absoluteViewPath.indexOf('/ccc/')) > -1) {
            if ((index = absoluteViewPath.indexOf('/views/')) > -1) {
                componentRoot = absoluteViewPath.substring(0, index);

                var componentPartialsObj = getPartialsObj(path.join(
                    componentRoot, "partials")); // 子component的partials
                return assign(globalPartials, componentPartialsObj);
            }
        } else {
            return globalPartials;
        }
    }
}

// 根据一个目录,返回对应的obj
// 扁平结构
function getPartialsObj(root) {
    var ret = {};
    processDir(root);
    return ret;

    function processDir(dir, keypath) {
        keypath = keypath || []; // default []

        fs.readdirSync(dir)
            .forEach(function (entry) {
                var entryPath = path.join(dir, entry);
                var s = fs.statSync(entryPath);

                if (s.isDirectory()) { // dir
                    processDir(entryPath, keypath.concat(entry));
                } else if (s.isFile() && entry.match(htmlExtReg)) { // file
                    // key
                    var entryWithNoExt = entry.replace(htmlExtReg, '');
                    var key = keypath.concat(entryWithNoExt)
                        .join(".");

                    // value
                    var template = rewriteComponentSource(entryPath, fs.readFileSync(
                        entryPath, 'utf-8'));

                    ret[key] = template;
                }
            });
    }
}

exports.engine = function (filePath, options, fn) {
    try {
        var template = fs.readFileSync(filePath, 'utf-8');
        template = rewrite({
            revPost: cRevPost('')
        }, template);
        template = rewriteComponentSource(filePath, template);
        var html = new Ractive({
            partials: options.partials,
            template: template, //TODO: production 优化，cache
            data: options
        })
            .toHTML();
        var appRoot = options.appRoot;
        if (appRoot && options.assetsDirName) {
            var libs = JSON.parse(fs.readFileSync(path.join(appRoot,
                options.assetsDirName,
                'js',
                'lib.json'), 'utf-8'))
                .map(function (lib) {
                    return path.resolve(path.join(appRoot,
                        'assets', 'js'), lib)
                        .substring(appRoot.length);
                });
            var libJsReplaced;
            html = html.replace(
                /(<script\s+src=["']?)\/assets\/js\/lib.js(["']?><\/script>)/g,
                function (all, p1, p2) {
                    return libJsReplaced ? '' : p1 + libs.join(p2 + p1) +
                        p2;
                });
        }
        fn(null, html);
    } catch (err) {
        fn(err);
    }
};

exports.middleware = function (opts) {
    var resolvedViewPath = {}; //TODO: refactory
    return function (req, res, next) {
        var reqPath = req.path.replace(/\/$/, '');
        if (res.viewPath) {
            return render(path.join(opts.appRoot, opts.viewsDirName, res.viewPath));
        }
        var viewPath = path.join(opts.appRoot, opts.viewsDirName, reqPath);
        if (env === 'production' && viewPath in
            resolvedViewPath) {
            var rvp = resolvedViewPath[viewPath];
            if (false === rvp) {
                return notFound();
            } else {
                return render(rvp);
            }
        }
        findViewAndRender('./views' + reqPath + '.html',
            function () {
                findViewAndRender('./views' + reqPath +
                    '/index.html', function () {
                        findViewAndRender(
                            './ccc/*/views' + reqPath +
                            '.html',
                            function () {
                                findViewAndRender(
                                    './ccc/*/views' +
                                    reqPath + '/index.html',
                                    notFound);
                            });
                    });
            });

        function notFound() { //TODO: fix it
            resolvedViewPath[viewPath] = false;
            next();
        }

        function findViewAndRender(viewPath, nf) {
            glob(viewPath, {
                cwd: opts.appRoot
            }, function (error, files) {
                if (!files.length) {
                    return nf();
                }
                var absoluteViewPath = path.join(opts.appRoot,
                    files[0]);

                res.locals.partials = getPartials(opts.appRoot,
                    absoluteViewPath);

                render(absoluteViewPath);
            });
        }

        function render(rvp) {
            if (env === 'production' && viewPath in
                resolvedViewPath) {
                resolvedViewPath[viewPath] = rvp;
            }
            res.render(rvp);
        }
    };
};

exports.argmentApp = function (app, opts) {
    app.set('view engine', 'html');
    app.engine('html', exports.engine);
    app.locals.appRoot = opts.appRoot;
    app.locals.assetsDirName = opts.assetsDirName;
    app.use(function (req, res, next) {
        var _render = res.render;
        res.render = function (view, options, fn) {
            if ('function' === typeof options) {
                fn = options;
                options = {};
            }
            this.locals.partials = this.locals.partials || getPartials(opts
                .appRoot);
            options = assign({}, app.locals, this.locals, options);
            this.locals = {};
            _render.call(this, view, options, fn);
        };
        next();
    });
    app.use(exports.middleware(opts));
};<|endoftext|>


<commit_before>var express = require('express'),
    path = require('path'),
    util = require('util'),
    exphbs = require('express-handlebars'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    session = require('express-session'),
    passport = require('passport'),
    LocalStrategy = require('passport-local'),
    TwitterStrategy = require('passport-twitter'),
    GoogleStrategy = require('passport-google'),
    FacebookStrategy = require('passport-facebook'),
    config = require('./config.js'), //tokens and private stuff
    funct = require('./functions.js'); //has helper functions for passport and db work

var app = express();

//================
//Passport middleware
//================

// Passport session setup.
passport.serializeUser(function(user, done) {
    console.log("serializing " + user.username);
    done(null, user);
});

passport.deserializeUser(function(obj, done) {
    console.log("deserializing " + obj);
    done(null, obj);
});

//Local sign-in
passport.use('local-signin', new LocalStrategy(
    {passReqToCallback: true},
    function(req, username, password, done){
        funct.localAuth(username, password, done)
        .then(function(user){
            if(user){
                console.log('logged in as: '+user.username);
                req.session.success = user.username + ' logged in.';
            } else {
                console.log('could not log in');
                req.session.error = 'Could not log user in.  Try again.'; //for user
            }
            done(null, user);
        })
        .fail(function(err){
            console.err('error: '+err.body);
        });
    }
));

//local sign-up
passport.use('local-signup', new LocalStrategy(
    {passReqToCallback: true},
    function(req, username, password, done){
        funct.localReg(username, password, done)
        .then(function(user){
            if(user){
                console.log('registered '+user.username);
                req.session.success = user.username + ' registered and logged in.';
            } else {
                console.log('could not register user');
                req.session.error = 'That username is taken! Please try a different one.'; //for user
            }
            done(null, user);
        })
        .fail(function(err){
            console.err('error: '+err.body);
        });
    }
));

//================
//Configure express
//================

app.use(logger('combined'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(session({secret: 'supernova', saveUninitialized: true, resave: true}));
app.use(passport.initialize());
app.use(passport.session());

// Session-persisted message middleware
app.use(function(req, res, next){
  var err = req.session.error,
      msg = req.session.notice,
      success = req.session.success;

  delete req.session.error;
  delete req.session.success;
  delete req.session.notice;

  if (err) { res.locals.error = err; }
  if (msg) { res.locals.notice = msg; }
  if (success)  { res.locals.success = success; }

  next();
});

app.use('/icons',express.static(path.join(__dirname, 'public/icons')));
app.use('/images',express.static(path.join(__dirname, 'public/images')));
app.use('/layouts',express.static(path.join(__dirname, 'views/layouts')));

// Configure express to use handlebars templates
var hbs = exphbs.create({
    defaultLayout: 'main',
});
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

//================
//Routes
//================

//home
app.get('/', function(req, res){
    funct.getContestants().then(function(results){
        res.render('home', {user: req.user, girls: results});
    }).fail(function(err){
        res.render('home', {user: req.user, girls: []});
        req.session.failure= 'Error fetching contestant database'+err.body;
    });
});

//rules page
app.get('/rules', function(req, res){
    res.render('rules', {});
});

//basic middleware for admin related pages to check for authentication first.
app.use('/admin', function(req, res, next){
    if(req.isAuthenticated()){ return next();}
    req.session.error = 'You must be signed in to view the requested page.';
    res.redirect('/signin');
});

//admin page
app.get('/admin', function(req, res){
    funct.getContestants().then(function(results){
        console.log(util.inspect(results));
        res.render('admin', {user: req.user, girls: results});
    }).fail(function(err){
        res.render('admin', {user: req.user, girls: []});
        req.session.failure= 'Error fetching contestant database'+err.body;
    });
});

//sign-in/up page
app.get('/signin', function(req, res){
    res.render('signin');
});

//handle signup route
app.post('/local-reg', passport.authenticate('local-signup', {
    successRedirect: '/',
    failureRedirect: '/signin'
}) );

//handle sign-in route
app.post('/login', passport.authenticate('local-signin', {
    successRedirect: '/',
    failureRedirect: '/signin'
}) );

//logout
app.get('/logout', function(req, res){
    var name = req.user.username;
    console.log("LOGGIN OUT " + req.user.username)
    req.logout();
    res.redirect('/');
    req.session.notice = "You have successfully been logged out " + name + "!";
});

//Start listening!
var port = 3000;
app.listen(port);
console.log('listening on ' + port);

<commit_msg>changes to facilitate heroku hosting<commit_after>var express = require('express'),
    path = require('path'),
    util = require('util'),
    exphbs = require('express-handlebars'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    session = require('express-session'),
    passport = require('passport'),
    LocalStrategy = require('passport-local'),
    TwitterStrategy = require('passport-twitter'),
    GoogleStrategy = require('passport-google'),
    FacebookStrategy = require('passport-facebook'),
    config = require('./config.js'), //tokens and private stuff
    funct = require('./functions.js'); //has helper functions for passport and db work

var app = express();

//================
//Passport middleware
//================

// Passport session setup.
passport.serializeUser(function(user, done) {
    console.log("serializing " + user.username);
    done(null, user);
});

passport.deserializeUser(function(obj, done) {
    console.log("deserializing " + obj);
    done(null, obj);
});

//Local sign-in
passport.use('local-signin', new LocalStrategy(
    {passReqToCallback: true},
    function(req, username, password, done){
        funct.localAuth(username, password, done)
        .then(function(user){
            if(user){
                console.log('logged in as: '+user.username);
                req.session.success = user.username + ' logged in.';
            } else {
                console.log('could not log in');
                req.session.error = 'Could not log user in.  Try again.'; //for user
            }
            done(null, user);
        })
        .fail(function(err){
            console.err('error: '+err.body);
        });
    }
));

//local sign-up
passport.use('local-signup', new LocalStrategy(
    {passReqToCallback: true},
    function(req, username, password, done){
        funct.localReg(username, password, done)
        .then(function(user){
            if(user){
                console.log('registered '+user.username);
                req.session.success = user.username + ' registered and logged in.';
            } else {
                console.log('could not register user');
                req.session.error = 'That username is taken! Please try a different one.'; //for user
            }
            done(null, user);
        })
        .fail(function(err){
            console.err('error: '+err.body);
        });
    }
));

//================
//Configure express
//================

app.use(logger('combined'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(session({secret: 'supernova', saveUninitialized: true, resave: true}));
app.use(passport.initialize());
app.use(passport.session());

// Session-persisted message middleware
app.use(function(req, res, next){
  var err = req.session.error,
      msg = req.session.notice,
      success = req.session.success;

  delete req.session.error;
  delete req.session.success;
  delete req.session.notice;

  if (err) { res.locals.error = err; }
  if (msg) { res.locals.notice = msg; }
  if (success)  { res.locals.success = success; }

  next();
});

app.set('port', (process.env.PORT || 5000));
app.use('/icons',express.static(path.join(__dirname, 'public/icons')));
app.use('/images',express.static(path.join(__dirname, 'public/images')));
app.use('/layouts',express.static(path.join(__dirname, 'views/layouts')));

// Configure express to use handlebars templates
var hbs = exphbs.create({
    defaultLayout: 'main',
});
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

//================
//Routes
//================

//home
app.get('/', function(req, res){
    funct.getContestants().then(function(results){
        res.render('home', {user: req.user, girls: results});
    }).fail(function(err){
        res.render('home', {user: req.user, girls: []});
        req.session.failure= 'Error fetching contestant database'+err.body;
    });
});

//rules page
app.get('/rules', function(req, res){
    res.render('rules', {});
});

//basic middleware for admin related pages to check for authentication first.
app.use('/admin', function(req, res, next){
    if(req.isAuthenticated()){ return next();}
    req.session.error = 'You must be signed in to view the requested page.';
    res.redirect('/signin');
});

//admin page
app.get('/admin', function(req, res){
    funct.getContestants().then(function(results){
        console.log(util.inspect(results));
        res.render('admin', {user: req.user, girls: results});
    }).fail(function(err){
        res.render('admin', {user: req.user, girls: []});
        req.session.failure= 'Error fetching contestant database'+err.body;
    });
});

//sign-in/up page
app.get('/signin', function(req, res){
    res.render('signin');
});

//handle signup route
app.post('/local-reg', passport.authenticate('local-signup', {
    successRedirect: '/',
    failureRedirect: '/signin'
}) );

//handle sign-in route
app.post('/login', passport.authenticate('local-signin', {
    successRedirect: '/',
    failureRedirect: '/signin'
}) );

//logout
app.get('/logout', function(req, res){
    var name = req.user.username;
    console.log("LOGGIN OUT " + req.user.username)
    req.logout();
    res.redirect('/');
    req.session.notice = "You have successfully been logged out " + name + "!";
});

//Start listening!
app.listen(app.get('port'), function(){
    console.log("Node app is running at localhost:" + app.get('port'));
});

<|endoftext|>


<commit_before>"use strict";

require("dotenv").config();
require('newrelic');

var express = require("express");
var helmet = require("helmet");
var readFile = require("fs-readfile-promise");
var components = require("server-components");

require("./components/item-feed/item-feed");
require("./components/item-carousel/item-carousel");
require("./components/manual-source");
require("./components/twitter-source");
require("./components/github-source");
require("./components/rss-source");
require("./components/oembed-item-wrapper/oembed-item-wrapper");
require("./components/social-media-icons");
require("./components/copyright-notice");
require("./components/google-analytics");

var app = express();

app.use(express.static('static'));
app.use(helmet());

app.get('/', function (req, res) {
    readFile("index.html").then((html) => {
        return components.renderPage(html);
    }).then((output) => {
        res.send(output);
    }).catch((error) => {
        res.send("Panic, failed to render. " + error);
    });
});

var port = (process.env.PORT || 8080);
app.listen(port, () => console.log(`Listening on ${port}`));
<commit_msg>Manually enable HSTS, since Helmet won't seem to<commit_after>"use strict";

require("dotenv").config();
require('newrelic');

var express = require("express");
var helmet = require("helmet");
var readFile = require("fs-readfile-promise");
var components = require("server-components");

require("./components/item-feed/item-feed");
require("./components/item-carousel/item-carousel");
require("./components/manual-source");
require("./components/twitter-source");
require("./components/github-source");
require("./components/rss-source");
require("./components/oembed-item-wrapper/oembed-item-wrapper");
require("./components/social-media-icons");
require("./components/copyright-notice");
require("./components/google-analytics");

var app = express();

app.use(express.static('static'));
app.use(helmet());

app.get('/', function (req, res) {
    res.set("Strict-Transport-Security", "max-age=31556926");

    readFile("index.html").then((html) => {
        return components.renderPage(html);
    }).then((output) => {
        res.send(output);
    }).catch((error) => {
        res.send("Panic, failed to render. " + error);
    });
});

var port = (process.env.PORT || 8080);
app.listen(port, () => console.log(`Listening on ${port}`));
<|endoftext|>


<commit_before>'use strict';

const types  = require('./lib/types');
const scopes = require('./lib/scopes');

module.exports = {
    rules    : {
        'body-leading-blank'  : [
            2,
            'always',
        ],
        'body-max-length'     : [
            1,
            'always',
            Infinity,
        ],
        'body-min-length'     : [
            1,
            'always',
            0,
        ],
        'body-tense'          : [
            2,
            'always',
            [
                'present-imperative',
            ],
        ],
        'footer-leading-blank': [
            2,
            'always',
        ],
        'footer-max-length'   : [
            1,
            'always',
            Infinity,
        ],
        'footer-min-length'   : [
            1,
            'always',
            0,
        ],
        'footer-tense'        : [
            2,
            'always',
            [
                'present-imperative',
            ],
        ],
        'header-max-length'   : [
            1,
            'always',
            72,
        ],
        'header-min-length'   : [
            1,
            'always',
            0,
        ],
        'lang'                : [
            1,
            'always',
            'eng',
        ],
        'scope-enum'          : [
            2,
            'always',
            scopes(),
        ],
        'scope-case'          : [
            2,
            'always',
            'lowerCase',
        ],
        'scope-empty'         : [
            1,
            'never',
        ],
        'scope-max-length'    : [
            1,
            'always',
            Infinity,
        ],
        'scope-min-length'    : [
            1,
            'always',
            0,
        ],
        'subject-case'        : [
            2,
            'always',
            'lowerCase',
        ],
        'subject-empty'       : [
            2,
            'never',
        ],
        'subject-full-stop'   : [
            2,
            'never',
        ],
        'subject-max-length'  : [
            1,
            'always',
            Infinity,
        ],
        'subject-min-length'  : [
            1,
            'always',
            0,
        ],
        'subject-tense'       : [
            2,
            'always',
            [
                'present-imperative',
            ],
        ],
        'type-enum'           : [
            2,
            'always',
            types(),
        ],
        'type-case'           : [
            2,
            'always',
            'lowerCase',
        ],
        'type-empty'          : [
            2,
            'never',
        ],
        'type-max-length'     : [
            1,
            'always',
            Infinity,
        ],
        'type-min-length'     : [
            1,
            'always',
            0,
        ],
    },
    wildcards: {
        merge  : [
            "/^(Merge pull request (.+?) from (.+?))|(Merge (.+?) into (.+?))$/",
        ],
        revert : [
            '/^revert: (.+)/',
        ],
    },
};
<commit_msg>turn the severities of `{body,footer}-tense` rules into warning<commit_after>'use strict';

const types  = require('./lib/types');
const scopes = require('./lib/scopes');

module.exports = {
    rules    : {
        'body-leading-blank'  : [
            2,
            'always',
        ],
        'body-max-length'     : [
            1,
            'always',
            Infinity,
        ],
        'body-min-length'     : [
            1,
            'always',
            0,
        ],
        'body-tense'          : [
            1,
            'always',
            [
                'present-imperative',
            ],
        ],
        'footer-leading-blank': [
            2,
            'always',
        ],
        'footer-max-length'   : [
            1,
            'always',
            Infinity,
        ],
        'footer-min-length'   : [
            1,
            'always',
            0,
        ],
        'footer-tense'        : [
            1,
            'always',
            [
                'present-imperative',
            ],
        ],
        'header-max-length'   : [
            1,
            'always',
            72,
        ],
        'header-min-length'   : [
            1,
            'always',
            0,
        ],
        'lang'                : [
            1,
            'always',
            'eng',
        ],
        'scope-enum'          : [
            2,
            'always',
            scopes(),
        ],
        'scope-case'          : [
            2,
            'always',
            'lowerCase',
        ],
        'scope-empty'         : [
            1,
            'never',
        ],
        'scope-max-length'    : [
            1,
            'always',
            Infinity,
        ],
        'scope-min-length'    : [
            1,
            'always',
            0,
        ],
        'subject-case'        : [
            2,
            'always',
            'lowerCase',
        ],
        'subject-empty'       : [
            2,
            'never',
        ],
        'subject-full-stop'   : [
            2,
            'never',
        ],
        'subject-max-length'  : [
            1,
            'always',
            Infinity,
        ],
        'subject-min-length'  : [
            1,
            'always',
            0,
        ],
        'subject-tense'       : [
            2,
            'always',
            [
                'present-imperative',
            ],
        ],
        'type-enum'           : [
            2,
            'always',
            types(),
        ],
        'type-case'           : [
            2,
            'always',
            'lowerCase',
        ],
        'type-empty'          : [
            2,
            'never',
        ],
        'type-max-length'     : [
            1,
            'always',
            Infinity,
        ],
        'type-min-length'     : [
            1,
            'always',
            0,
        ],
    },
    wildcards: {
        merge  : [
            "/^(Merge pull request (.+?) from (.+?))|(Merge (.+?) into (.+?))$/",
        ],
        revert : [
            '/^revert: (.+)/',
        ],
    },
};
<|endoftext|>


<commit_before>var express,app,helmet,morgan,mongoose,router;
global._=require("lodash");
global.Promise=require("bluebird");
global.config=require("./config/config");
express=require("express");
helmet=require("helmet");
morgan=require("morgan")("dev");
mongoose=require("mongoose");
Promise.promisifyAll(mongoose.Model);
Promise.promisifyAll(mongoose.Model.prototype);
Promise.promisifyAll(mongoose.Query.prototype);
app=express();
app.disable("x-powered-by");
app.enable("strict routing");
app.set("view engine","jade");
app.use(morgan);
app.use(helmet.frameguard("deny"));
app.use(helmet.nosniff());

app.use("/api",require("./api"));
app.use(
  function(req,res,next){
    var n=req.url.indexOf(".");
    if(n===-1){res.sendFile(__dirname+"/public/index.html");}
    else{next();}
  },
  express.static("./public")
);
app.use(function(err,req,res,next){
  console.error(err.stack);
  return res.sendStatus(err.status||500);
});
mongoose.connect("mongodb://"+config.db.host+":27017/"+config.db.database);
app.listen(config.port,config.host);
console.log("listening on port %s",config.port);<commit_msg>change<commit_after>var express,app,helmet,morgan,mongoose,router;
global._=require("lodash");
global.Promise=require("bluebird");
global.config=require("./config/config");
express=require("express");
helmet=require("helmet");
morgan=require("morgan")("dev");
mongoose=require("mongoose");
Promise.promisifyAll(mongoose.Model);
Promise.promisifyAll(mongoose.Model.prototype);
Promise.promisifyAll(mongoose.Query.prototype);
app=express();
app.disable("x-powered-by");
app.enable("strict routing");
app.set("view engine","jade");
app.use(morgan);
app.use(helmet.frameguard("deny"));
app.use(helmet.nosniff());

app.use("/api",require("./api"));
app.use(
  function(req,res,next){
    var n=req.url.indexOf(".");
    if(n===-1){res.sendFile(__dirname+"/public/index.html");}
    else{next();}
  },
  express.static("./public")
);
app.use(function(err,req,res,next){
  console.error(err.stack);
  return res.sendStatus(err.status||500);
});
mongoose.connect("mongodb://"+config.db.host+":27017/"+config.db.database);
app.listen(config.port,config.host);
console.log("listening on %s:%s",config.host,config.port);<|endoftext|>