<commit_before>
const RG = require('./rg.js');
const debug = require('debug')('bitn:Component');

RG.Component = {};

RG.Component.createdCompDecls = {};

// Used by components which cannot be serialized
// In your component, add the following:
//   this.toJSON = NO_SERIALISATION;
const NO_SERIALISATION = () => null;
RG.Component.NO_SERIALISATION = NO_SERIALISATION;

/* Can be used to create simple Component object constructors with no other data
 * fields. Usage:
 *   const MyComponent = TagComponent('MyComponent');
 *   const compInst = new MyComponent();
 */
const TagComponent = function(type, obj = {}) {
    errorIfCompDeclExists(type);
    const CompDecl = function() {
        RG.Component.Base.call(this, type);
        Object.keys(obj).forEach(key => {
            this[key] = obj[key];
        });
    };
    RG.extend2(CompDecl, RG.Component.Base);
    RG.Component.createdCompDecls[type] = CompDecl;
    return CompDecl;
};
RG.Component.TagComponent = TagComponent;

/* Can be used to create simple data components with setters/getters.
 * Usage:
 *   const Immunity = DataComponent('Immunity', {value: 1, dmgType: 'Fire'});
 *   const immunityComp = new Immunity();
 *   immunityComp.setDmgType('Fire')
 *   ...etc
 * NOTE: There's difference between members and compAttrib. Component
 * attributes are things like serialisation and uniqueness.
 * (only one comp per entity of that type). There are convenience functions
 * below to define unique and non-serialisable (Transient) components. See
 * TransientTagComponent and TransientDataComponent below.
 */

const DataComponent = (type, members, compAttrib = {}) => {
    errorIfCompDeclExists(type);
    if (typeof members !== 'object' || Array.isArray(members)) {
        RG.err('component.js', `DataComponent: ${type}`,
            'Members must be given as key/value pairs.');
    }

    // This is the constructor function to be returned
    const CompDecl = function(...argsList) {
        RG.Component.Base.call(this, type);
        Object.keys(compAttrib).forEach(key => {
            this[key] = compAttrib[key];
        });
        Object.keys(members).forEach(key => {
            if (argsList && argsList[0] && argsList[0].hasOwnProperty(key)) {
                this[key] = argsList[0][key];
            }
            else if (typeof members[key] === 'object') {
                // Unless cloned, the object ref is same for all
                // instances of this component.
                this[key] = JSON.parse(JSON.stringify(members[key]));
            }
            else {
                this[key] = members[key];
            }
        });
        // User can define _init function if complex initialisation required
        // For example, onAdd/onRemove callbacks can be given here
        if (this._init && typeof this._init === 'function') {
            this._init(...argsList);
        }
    };
    RG.extend2(CompDecl, RG.Component.Base);

    // Create the member functions for prototype
    Object.keys(members).forEach(propName => {
        // Check that we are not overwriting anything in base class
        if (RG.Component.Base.prototype.hasOwnProperty(propName)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${propName} is reserved in Component.Base`);
        }

        // Create the getter method unless it exists in Base
        const setter = 'set' + propName.capitalize();
        if (RG.Component.Base.prototype.hasOwnProperty(setter)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${setter} is reserved in Component.Base`);
        }
        CompDecl.prototype[setter] = function(value) {
            this[propName] = value;
        };

        // Create the getter method unless it exists in Base
        const getter = 'get' + propName.capitalize();
        if (RG.Component.Base.prototype.hasOwnProperty(setter)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${getter} is reserved in Component.Base`);
        }
        CompDecl.prototype[getter] = function() {
            return this[propName];
        };
    });

    RG.Component.createdCompDecls[type] = CompDecl;
    return CompDecl;
};
RG.Component.DataComponent = DataComponent;

/* Same TagComponent, except only one per entity is preserved. Adding another
 * will remove the existing one. */
const UniqueTagComponent = type => {
    return TagComponent(type, {_isUnique: true});
};
RG.Component.UniqueTagComponent = UniqueTagComponent;

/* Same DataComponent, except only one per entity is preserved. Adding another
 * will remove the existing one. */
const UniqueDataComponent = (type, members) => {
    return DataComponent(type, members, {_isUnique: true});
};
RG.Component.UniqueDataComponent = UniqueDataComponent;

/* Same as TagComponent but removes serialisation. This component is used by
* systems for transient stuff like Attacks, Move and SpellCasting. */
const TransientTagComponent = type => {
    return TagComponent(type, {toJSON: NO_SERIALISATION});
};
RG.Component.TransientTagComponent = TransientTagComponent;

/* Same as TransientTagComponent, but allows specifying data fields. */
const TransientDataComponent = (type, members) => {
    return DataComponent(type, members, {toJSON: NO_SERIALISATION});
};
RG.Component.TransientDataComponent = TransientDataComponent;

// TODO UniqueTransientDataComponent
// TODO UniqueTransientTagComponent

/* Raises an error if two comp declarations with same type are created. */
function errorIfCompDeclExists(type) {
    if (RG.Component.createdCompDecls[type]) {
        RG.err('RG.Component', 'Tag/DataComponent',
            `Duplicate decl: ${type}`);
    }
}
//---------------------------------------------------------------------------
// ECS COMPONENTS
//---------------------------------------------------------------------------

/* Important Guidelines:
 * =====================
 *
 *  A component constructor must NOT take any
 *  parameters. Call Base constructor with the type. (which must be identical
 *  to the Object type). Don't forget extend2() at the end. See existing comps
 *  for details.
 *
 *  To benefit from serialisation, all methods should be named:
 *    setXXX - getXXX
 *  Note that if you have any methods starting with set/get, these are used in
 *  the serialisation UNLESS you override toJSON() method.
 *
 *  If only one instance of component should exist for an entity, set
 *    this._unique = true.
 *  inside the component.
 *
 *  If serialisation using toJSON is completely undesirable, use the following:
 *    this.toJSON = NO_SERIALISATION;
 *  inside your component.
 *
 *  WARNING: don't mess with or override getType/setType functions. This will
 *  almost certainly break the logic.
 *
 *  If the component requires refs to other custom objects (ie Entities, Comps),
 *  you must write custom toJSON(), and use RG.getObjRef() for serialize those
 *  fields.
 */

/* Given an entity, serializes its components. */
RG.Component.compsToJSON = ent => {
    const components = {};
    const thisComps = ent.getComponents();
    Object.keys(thisComps).forEach(id => {
        const compJson = thisComps[id].toJSON();
        if (compJson) {
            components[id] = compJson;
        }
    });
    return components;
};

RG.Component.idCount = 0;

/* Base class for all components. Provides callback hooks, copying and cloning.
 * */
RG.Component.Base = function(type) {
    this._type = type;
    this._entity = null;
    this._id = RG.Component.idCount++;
    this._isUnique = false;

    this._onAddCallbacks = [];
    this._onRemoveCallbacks = [];
};

RG.Component.Base.prototype.getID = function() {return this._id;};
RG.Component.Base.prototype.setID = function(id) {this._id = id;};

RG.Component.Base.prototype.getEntity = function() {return this._entity;};
RG.Component.Base.prototype.setEntity = function(entity) {
    if (this._entity === null && entity !== null) {
        this._entity = entity;
    }
    else if (entity === null) {
        this._entity = null;
    }
    else {
        RG.err('Component.Base', 'setEntity', 'Entity already set.');
    }
};

RG.Component.Base.prototype.isUnique = function() {return this._isUnique;};

RG.Component.Base.prototype.getType = function() {return this._type;};
RG.Component.Base.prototype.setType = function(type) {this._type = type;};

// Called when a component is added to the entity
RG.Component.Base.prototype.entityAddCallback = function(entity) {
    this.setEntity(entity);
    for (let i = 0; i < this._onAddCallbacks.length; i++) {
        this._onAddCallbacks[i]();
    }
};

// Called when a component is removed from the entity
RG.Component.Base.prototype.entityRemoveCallback = function() {
    for (let i = 0; i < this._onRemoveCallbacks.length; i++) {
        this._onRemoveCallbacks[i]();
    }
    this.setEntity(null);
};

RG.Component.Base.prototype.addCallback = function(name, cb) {
    if (name === 'onAdd') {this._onAddCallbacks.push(cb);}
    else if (name === 'onRemove') {this._onRemoveCallbacks.push(cb);}
    else {
        RG.err('Component.Base',
            'addCallback', 'CB name ' + name + ' invalid.');
    }
};

/* Removes all callbacks of given type. */
RG.Component.Base.prototype.removeCallbacks = function(name) {
    if (name === 'onAdd') {
        this._onAddCallbacks = [];
    }
    else if (name === 'onRemove') {
        this._onRemoveCallbacks = [];
    }
};

/* Works correctly for any component having only simple getters and setters. For
 * more complex components, roll out a separate clone function. */
RG.Component.Base.prototype.clone = function() {
    const compType = this.getType();
    if (RG.Component.hasOwnProperty(compType)) {
        const comp = new RG.Component[compType]();
        // const newID = comp.getID();
        comp.copy(this);
        // comp.setID(newID);
        return comp;
    }
    else {
        RG.err('Component.Base', 'clone',
            `No type |${compType}| in RG.Component.`);
    }
    return null;
};

/* Works for any component implementing getXXX/setXXX functions. Does a shallow
 * copy of properties only though. */
RG.Component.Base.prototype.copy = function(rhs) {
    for (const p in this) {
        if (/^get/.test(p)) {
            const getter = p;
            if (getter !== 'getEntity' && getter !== 'getID') {
                const setter = getter.replace('get', 'set');
                if (typeof rhs[getter] === 'function') {
                    if (typeof this[setter] === 'function') {
                        const attrVal = rhs[getter]();
                        this[setter](attrVal);
                    }
                }
            }
        }
    }
};

RG.Component.Base.prototype.equals = function(rhs) {
    return this.getType() === rhs.getType();
};

RG.Component.Base.prototype.toString = function() {
    return 'Component: ' + this.getType();
};

/* Creates a simple JSON representation of the component. NOTE: This relies on
 * getters and setters being named similarly, ie getABC/setABC! Don't rely on
 * this function if you need something more sophisticated. */
RG.Component.Base.prototype.toJSON = function() {
    const obj = {};
    for (const p in this) {
        if (/^get/.test(p)) {
            const getter = p;
            if (getter !== 'getEntity') {
                if (typeof this[getter] === 'function') {
                    const setter = getter.replace('get', 'set');
                    if (typeof this[setter] === 'function') {
                        // To de-serialize, we can then do
                        //   obj[setter](json[setter])
                        obj[setter] = this[getter]();
                    }
                }
            }
        }
    }
    return obj;
};

/* Action component is added to all schedulable acting entities.*/
RG.Component.Action = TransientDataComponent('Action',
    {energy: 0, active: false});

RG.Component.Action.prototype.addEnergy = function(energy) {
    this.energy += energy;
};

RG.Component.Action.prototype.resetEnergy = function() {this.energy = 0;};

RG.Component.Action.prototype.enable = function() {
    if (this.active === false) {
        RG.POOL.emitEvent(RG.EVT_ACT_COMP_ENABLED,
            {actor: this.getEntity()});
        this.active = true;
    }
    else {
        const name = this.getEntity().getName();
        const id = this.getEntity().getID();
        const entInfo = `${name} ${id}`;
        debug(`Action already active for ${entInfo}`);
    }
};

RG.Component.Action.prototype.disable = function() {
    if (this.active === true) {
        RG.POOL.emitEvent(RG.EVT_ACT_COMP_DISABLED,
            {actor: this.getEntity()});
        this.active = false;
    }
};

RG.Component.Action.prototype.entityAddCallback = function(entity) {
    RG.Component.Base.prototype.entityAddCallback.call(this, entity);
};

RG.Component.Action.prototype.entityRemoveCallback = function(entity) {
    RG.Component.Base.prototype.entityRemoveCallback.call(this, entity);
};

/* Factory function that should be used instead of new RG.Component[varName]. */
RG.Component.create = function(compName, ...args) {
    if (RG.Component[compName]) {
        return new RG.Component[compName](...args);
    }
    RG.err('RG.Component', 'create',
        `Comp type |${compName}| does not exist.`);
    return null;
};

module.exports = RG.Component;
<commit_msg>Removed 2 unused functions.<commit_after>
const RG = require('./rg.js');
const debug = require('debug')('bitn:Component');

RG.Component = {};

RG.Component.createdCompDecls = {};

// Used by components which cannot be serialized
// In your component, add the following:
//   this.toJSON = NO_SERIALISATION;
const NO_SERIALISATION = () => null;
RG.Component.NO_SERIALISATION = NO_SERIALISATION;

/* Can be used to create simple Component object constructors with no other data
 * fields. Usage:
 *   const MyComponent = TagComponent('MyComponent');
 *   const compInst = new MyComponent();
 */
const TagComponent = function(type, obj = {}) {
    errorIfCompDeclExists(type);
    const CompDecl = function() {
        RG.Component.Base.call(this, type);
        Object.keys(obj).forEach(key => {
            this[key] = obj[key];
        });
    };
    RG.extend2(CompDecl, RG.Component.Base);
    RG.Component.createdCompDecls[type] = CompDecl;
    return CompDecl;
};
RG.Component.TagComponent = TagComponent;

/* Can be used to create simple data components with setters/getters.
 * Usage:
 *   const Immunity = DataComponent('Immunity', {value: 1, dmgType: 'Fire'});
 *   const immunityComp = new Immunity();
 *   immunityComp.setDmgType('Fire')
 *   ...etc
 * NOTE: There's difference between members and compAttrib. Component
 * attributes are things like serialisation and uniqueness.
 * (only one comp per entity of that type). There are convenience functions
 * below to define unique and non-serialisable (Transient) components. See
 * TransientTagComponent and TransientDataComponent below.
 */

const DataComponent = (type, members, compAttrib = {}) => {
    errorIfCompDeclExists(type);
    if (typeof members !== 'object' || Array.isArray(members)) {
        RG.err('component.js', `DataComponent: ${type}`,
            'Members must be given as key/value pairs.');
    }

    // This is the constructor function to be returned
    const CompDecl = function(...argsList) {
        RG.Component.Base.call(this, type);
        Object.keys(compAttrib).forEach(key => {
            this[key] = compAttrib[key];
        });
        Object.keys(members).forEach(key => {
            if (argsList && argsList[0] && argsList[0].hasOwnProperty(key)) {
                this[key] = argsList[0][key];
            }
            else if (typeof members[key] === 'object') {
                // Unless cloned, the object ref is same for all
                // instances of this component.
                this[key] = JSON.parse(JSON.stringify(members[key]));
            }
            else {
                this[key] = members[key];
            }
        });
        // User can define _init function if complex initialisation required
        // For example, onAdd/onRemove callbacks can be given here
        if (this._init && typeof this._init === 'function') {
            this._init(...argsList);
        }
    };
    RG.extend2(CompDecl, RG.Component.Base);

    // Create the member functions for prototype
    Object.keys(members).forEach(propName => {
        // Check that we are not overwriting anything in base class
        if (RG.Component.Base.prototype.hasOwnProperty(propName)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${propName} is reserved in Component.Base`);
        }

        // Create the getter method unless it exists in Base
        const setter = 'set' + propName.capitalize();
        if (RG.Component.Base.prototype.hasOwnProperty(setter)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${setter} is reserved in Component.Base`);
        }
        CompDecl.prototype[setter] = function(value) {
            this[propName] = value;
        };

        // Create the getter method unless it exists in Base
        const getter = 'get' + propName.capitalize();
        if (RG.Component.Base.prototype.hasOwnProperty(setter)) {
            RG.err('component.js', `DataComponent: ${type}`,
                `${getter} is reserved in Component.Base`);
        }
        CompDecl.prototype[getter] = function() {
            return this[propName];
        };
    });

    RG.Component.createdCompDecls[type] = CompDecl;
    return CompDecl;
};
RG.Component.DataComponent = DataComponent;

/* Same TagComponent, except only one per entity is preserved. Adding another
 * will remove the existing one. */
const UniqueTagComponent = type => {
    return TagComponent(type, {_isUnique: true});
};
RG.Component.UniqueTagComponent = UniqueTagComponent;

/* Same DataComponent, except only one per entity is preserved. Adding another
 * will remove the existing one. */
const UniqueDataComponent = (type, members) => {
    return DataComponent(type, members, {_isUnique: true});
};
RG.Component.UniqueDataComponent = UniqueDataComponent;

/* Same as TagComponent but removes serialisation. This component is used by
* systems for transient stuff like Attacks, Move and SpellCasting. */
const TransientTagComponent = type => {
    return TagComponent(type, {toJSON: NO_SERIALISATION});
};
RG.Component.TransientTagComponent = TransientTagComponent;

/* Same as TransientTagComponent, but allows specifying data fields. */
const TransientDataComponent = (type, members) => {
    return DataComponent(type, members, {toJSON: NO_SERIALISATION});
};
RG.Component.TransientDataComponent = TransientDataComponent;

// TODO UniqueTransientDataComponent
// TODO UniqueTransientTagComponent

/* Raises an error if two comp declarations with same type are created. */
function errorIfCompDeclExists(type) {
    if (RG.Component.createdCompDecls[type]) {
        RG.err('RG.Component', 'Tag/DataComponent',
            `Duplicate decl: ${type}`);
    }
}
//---------------------------------------------------------------------------
// ECS COMPONENTS
//---------------------------------------------------------------------------

/* Important Guidelines:
 * =====================
 *
 *  A component constructor must NOT take any
 *  parameters. Call Base constructor with the type. (which must be identical
 *  to the Object type). Don't forget extend2() at the end. See existing comps
 *  for details.
 *
 *  To benefit from serialisation, all methods should be named:
 *    setXXX - getXXX
 *  Note that if you have any methods starting with set/get, these are used in
 *  the serialisation UNLESS you override toJSON() method.
 *
 *  If only one instance of component should exist for an entity, set
 *    this._unique = true.
 *  inside the component.
 *
 *  If serialisation using toJSON is completely undesirable, use the following:
 *    this.toJSON = NO_SERIALISATION;
 *  inside your component.
 *
 *  WARNING: don't mess with or override getType/setType functions. This will
 *  almost certainly break the logic.
 *
 *  If the component requires refs to other custom objects (ie Entities, Comps),
 *  you must write custom toJSON(), and use RG.getObjRef() for serialize those
 *  fields.
 */

/* Given an entity, serializes its components. */
RG.Component.compsToJSON = ent => {
    const components = {};
    const thisComps = ent.getComponents();
    Object.keys(thisComps).forEach(id => {
        const compJson = thisComps[id].toJSON();
        if (compJson) {
            components[id] = compJson;
        }
    });
    return components;
};

RG.Component.idCount = 0;

/* Base class for all components. Provides callback hooks, copying and cloning.
 * */
RG.Component.Base = function(type) {
    this._type = type;
    this._entity = null;
    this._id = RG.Component.idCount++;
    this._isUnique = false;

    this._onAddCallbacks = [];
    this._onRemoveCallbacks = [];
};

RG.Component.Base.prototype.getID = function() {return this._id;};
RG.Component.Base.prototype.setID = function(id) {this._id = id;};

RG.Component.Base.prototype.getEntity = function() {return this._entity;};
RG.Component.Base.prototype.setEntity = function(entity) {
    if (this._entity === null && entity !== null) {
        this._entity = entity;
    }
    else if (entity === null) {
        this._entity = null;
    }
    else {
        RG.err('Component.Base', 'setEntity', 'Entity already set.');
    }
};

RG.Component.Base.prototype.isUnique = function() {return this._isUnique;};

RG.Component.Base.prototype.getType = function() {return this._type;};
RG.Component.Base.prototype.setType = function(type) {this._type = type;};

// Called when a component is added to the entity
RG.Component.Base.prototype.entityAddCallback = function(entity) {
    this.setEntity(entity);
    for (let i = 0; i < this._onAddCallbacks.length; i++) {
        this._onAddCallbacks[i]();
    }
};

// Called when a component is removed from the entity
RG.Component.Base.prototype.entityRemoveCallback = function() {
    for (let i = 0; i < this._onRemoveCallbacks.length; i++) {
        this._onRemoveCallbacks[i]();
    }
    this.setEntity(null);
};

RG.Component.Base.prototype.addCallback = function(name, cb) {
    if (name === 'onAdd') {this._onAddCallbacks.push(cb);}
    else if (name === 'onRemove') {this._onRemoveCallbacks.push(cb);}
    else {
        RG.err('Component.Base',
            'addCallback', 'CB name ' + name + ' invalid.');
    }
};

/* Removes all callbacks of given type. */
RG.Component.Base.prototype.removeCallbacks = function(name) {
    if (name === 'onAdd') {
        this._onAddCallbacks = [];
    }
    else if (name === 'onRemove') {
        this._onRemoveCallbacks = [];
    }
};

/* Works correctly for any component having only simple getters and setters. For
 * more complex components, roll out a separate clone function. */
RG.Component.Base.prototype.clone = function() {
    const compType = this.getType();
    if (RG.Component.hasOwnProperty(compType)) {
        const comp = new RG.Component[compType]();
        // const newID = comp.getID();
        comp.copy(this);
        // comp.setID(newID);
        return comp;
    }
    else {
        RG.err('Component.Base', 'clone',
            `No type |${compType}| in RG.Component.`);
    }
    return null;
};

/* Works for any component implementing getXXX/setXXX functions. Does a shallow
 * copy of properties only though. */
RG.Component.Base.prototype.copy = function(rhs) {
    for (const p in this) {
        if (/^get/.test(p)) {
            const getter = p;
            if (getter !== 'getEntity' && getter !== 'getID') {
                const setter = getter.replace('get', 'set');
                if (typeof rhs[getter] === 'function') {
                    if (typeof this[setter] === 'function') {
                        const attrVal = rhs[getter]();
                        this[setter](attrVal);
                    }
                }
            }
        }
    }
};

RG.Component.Base.prototype.equals = function(rhs) {
    return this.getType() === rhs.getType();
};

RG.Component.Base.prototype.toString = function() {
    return 'Component: ' + this.getType();
};

/* Creates a simple JSON representation of the component. NOTE: This relies on
 * getters and setters being named similarly, ie getABC/setABC! Don't rely on
 * this function if you need something more sophisticated. */
RG.Component.Base.prototype.toJSON = function() {
    const obj = {};
    for (const p in this) {
        if (/^get/.test(p)) {
            const getter = p;
            if (getter !== 'getEntity') {
                if (typeof this[getter] === 'function') {
                    const setter = getter.replace('get', 'set');
                    if (typeof this[setter] === 'function') {
                        // To de-serialize, we can then do
                        //   obj[setter](json[setter])
                        obj[setter] = this[getter]();
                    }
                }
            }
        }
    }
    return obj;
};

/* Action component is added to all schedulable acting entities.*/
RG.Component.Action = TransientDataComponent('Action',
    {energy: 0, active: false});

RG.Component.Action.prototype.addEnergy = function(energy) {
    this.energy += energy;
};

RG.Component.Action.prototype.resetEnergy = function() {this.energy = 0;};

RG.Component.Action.prototype.enable = function() {
    if (this.active === false) {
        RG.POOL.emitEvent(RG.EVT_ACT_COMP_ENABLED,
            {actor: this.getEntity()});
        this.active = true;
    }
    else {
        const name = this.getEntity().getName();
        const id = this.getEntity().getID();
        const entInfo = `${name} ${id}`;
        debug(`Action already active for ${entInfo}`);
    }
};

RG.Component.Action.prototype.disable = function() {
    if (this.active === true) {
        RG.POOL.emitEvent(RG.EVT_ACT_COMP_DISABLED,
            {actor: this.getEntity()});
        this.active = false;
    }
};

/* Factory function that should be used instead of new RG.Component[varName]. */
RG.Component.create = function(compName, ...args) {
    if (RG.Component[compName]) {
        return new RG.Component[compName](...args);
    }
    RG.err('RG.Component', 'create',
        `Comp type |${compName}| does not exist.`);
    return null;
};

module.exports = RG.Component;
<|endoftext|>


<commit_before>var fs           =  require('fs');
var async        =  require('async');
var profanity    =  require('./profanity');
var Bitstamp     =  require('./Bitstamp');
var Blockchain   =  require('./Blockchain');

var Client       =  require('./Client');
var Lib          =  require('./Lib');
var Db           =  require('./Db');
var Config       =  require('./Config')();

// Command syntax
var cmdReg = /^!([a-zA-z]*)\s*(.*)$/;

function Shiba() {
  this.client = new Client(Config);

  this.setupUsernameScrape();
  this.setupConsoleLog();
  this.setupLossStreakComment();
  this.setupScamComment();
  this.setupBlockchain();
}

Shiba.prototype.setupUsernameScrape = function() {
  var self = this;
  self.client.on('player_bet', function(data) {
    Db.putUsername(data.username);
  });
  self.client.on('msg', function(msg) {
    if (msg.type != 'say') return;
    Db.putUsername(msg.username);
    Db.updateSeen(msg.username, msg);
    self.onSay(msg);
  });
};

Shiba.prototype.setupConsoleLog = function() {
  var self = this;
  self.client.on('game_starting', function(info) {
    var line = "Starting " + info.game_id
        + " " + info.hash.substring(0,8);
    process.stdout.write(line);
  });

  self.client.on('game_started', function(data) {
    process.stdout.write(".. ");
  });

  self.client.on('game_crash', function(data) {
    var gameInfo = self.client.getGameInfo();
    var crash = (data.game_crash/100).toFixed(2)
    process.stdout.write(" @" + crash + "x " + gameInfo.verified + "\n");
  });
};

Shiba.prototype.setupLossStreakComment = function() {
  var self = this;
  self.client.on('game_crash', function(data) {
    var gameHistory = self.client.gameHistory;

    // Determine loss streak
    var streak = gameHistory.length > 3;
    for (var i = 0; i < Math.min(gameHistory.length - 1, 4); ++i)
      streak = streak && gameHistory[i].game_crash <= 130;

    // Don't repeat yourself in a streak
    streak = streak && (gameHistory.length < 4 ||
                        gameHistory[i + 1].game_crash > 130);

    if (streak) self.client.doSay("wow. such rape. very butthurt");
  });
};

Shiba.prototype.setupScamComment = function() {
  var self = this;
  self.client.on('game_crash', function(data) {
    var gameInfo = self.client.getGameInfo();
    console.assert(gameInfo.hasOwnProperty('verified'));
    if (gameInfo.verified != 'ok') {
      self.client.doSay('wow. such scam. very hash failure.');
    }
  });
};

Shiba.prototype.setupBlockchain = function() {
  var bc = new Blockchain();

  bc.on('block', function(block) {
    Db.put('block', block);
  });
};

Shiba.prototype.getChatMessages = function(username, after) {
  var messages = [];
  var chatHistory = this.client.chatHistory;
  for (var i = 0; i < chatHistory.length; ++i) {
    then = new Date(chatHistory[i].time);
    if (then < after) break;

    if (chatHistory[i].username == username)
      messages.push(chatHistory[i]);
  }
  return messages;
};

Shiba.prototype.onSay = function(msg) {
  if (msg.username == this.client.username) return;

  // Coinurl blocking
  var coinurlReg = /http:\/\/cur\.lv\/.*/i;
  var coinurlMatch = msg.message.match(coinurlReg);
  if (coinurlMatch) return this.client.doMute(msg.username, '6h');

  // Minefield blocking
  var minefieldReg = /minefield\.bitcoinlab\.org\/.*(\?|&)r=/i;
  var minefieldMatch = msg.message.match(minefieldReg);
  if (minefieldMatch) return this.client.doMute(msg.username, '6h');

  // Cloud mining
  var cloudmineReg = /cloud.*mining.*http:\/\/vk\.cc\/[0-9a-z]/i;
  var cloudmineMatch = msg.message.match(minefieldReg);
  if (cloudmineMatch) return this.client.doMute(msg.username, '6h');

  var after, messages;
  // Rate limiter 5 messages in 5s
  after    = new Date(Date.now() - 5000);
  messages = this.getChatMessages(msg.username, after);
  if (messages.length >= 6) return this.client.doMute(msg.username, '15m');

  // Rate limiter 8 messages in 12s
  after    = new Date(Date.now() - 12000);
  messages = this.getChatMessages(msg.username, after);
  if (messages.length >= 9) return this.client.doMute(msg.username, '15m');

  var cmdMatch = msg.message.match(cmdReg);
  if (cmdMatch) this.onCmd(msg, cmdMatch[1], cmdMatch[2]);
};

Shiba.prototype.onCmd = function(msg, cmd, rest) {
  // Cmd rate limiter
  var after    = new Date(Date.now() - 10000);
  var messages = this.getChatMessages(msg.username, after);
  var rate = 0;

  for (var i = 0; i < messages.length; ++i)
    if (messages[i].message.match(cmdReg)) ++rate;

  if (rate >= 4)
    return this.client.doMute(msg.username, '5m');
  else if (rate >= 3)
    return this.client.doSay('bites ' + msg.username);

  switch(cmd) {
  case 'custom': this.onCmdCustom(msg, rest); break;
  case 'lick': this.onCmdLick(msg, rest); break;
  case 'seen': this.onCmdSeen(msg, rest); break;
  case 'convert': this.onCmdConvert(msg, rest); break;
  case 'block': this.onCmdBlock(msg, rest); break;
  }
};

Shiba.prototype.onCmdCustom = function(msg, rest) {
  var self = this;
  if (msg.role != 'admin' &&
      msg.role != 'moderator') return;

  var customReg   = /^([A-Za-z0-9]*)\s*(.*)$/;
  var customMatch = rest.match(customReg);
  var customUser  = customMatch[1];
  var customMsg   = customMatch[2];
  Db.addCustomLickMessage(customUser, customMsg, function (err) {
    if (err) {
      console.log('onCmdCustom:', err);
      self.client.doSay('wow. such leveldb fail');
    } else {
      self.client.doSay('wow. so cool. very obedient');
    }
  });
};

Shiba.prototype.onCmdLick = function(msg, user) {
  var self = this;
  user = user.toLowerCase();
  user = user.replace(/^\s+|\s+$/g,'');

  // We're cultivated and don't lick ourselves.
  if (user === self.client.username.toLowerCase()) return;

  if (profanity[user]) {
    self.client.doSay('so trollol. very annoying. such mute');
    self.client.doMute(msg.username, '5m');
    return;
  }

  async.parallel(
    [ function(cb) { Db.getUsername(user, cb); },
      function(cb) { Db.getCustomLickMessages(user, cb); }
    ],
    function (err, val) {
      if (err) return;

      var username = val[0];
      var customs  = val[1];
      customs.push('licks ' + username);
      var r = Math.random() * (customs.length - 0.8);
      var m = customs[Math.floor(r)];
      self.client.doSay(m);
    });
};

Shiba.prototype.onCmdSeen = function(msg, user) {
  var self = this;
  user = user.toLowerCase();
  user = user.replace(/^\s+|\s+$/g,'');

  // Special treatment of block.
  if (user === 'block') return this.onCmdBlock();

  // Avoid this.
  if (user === self.client.username.toLowerCase()) return;

  if (profanity[user]) {
    self.client.doSay('so trollol. very annoying. such mute');
    self.client.doMute(msg.username, '5m');
    return;
  }

  async.parallel(
    [ function(cb) { Db.getUsername(user, cb); },
      function(cb) { Db.getSeen(user, cb); }
    ],
    function (err, val) {
      if (err) return;

      var username = val[0];
      var msg      = val[1];
      var time     = new Date(msg.time);
      var diff     = Date.now() - time;

      var line;
      if (diff < 1000) {
        line = 'Seen ' + username + ' just now.';
      } else {
        line = 'Seen ' + username + ' ';
        line += Lib.formatTimeDiff(diff)
        line += ' ago.';
      }

      self.client.doSay(line);
    });
};

Shiba.prototype.onCmdConvert = function(msg, conv) {
  var self = this;
  conv = conv.replace(/^\s+|\s+$/g,'');
  var convReg = /^((-|\+)?[0-9]*\.?[0-9]*)(k?)\s*(bits?|btc)$/i;
  var convMatch = conv.match(convReg);

  if (convMatch) {
    Bitstamp.getAveragePrice(function(err, price) {

      console.log(convMatch);
      var amount   = parseFloat(convMatch[1], 10);
      var modifier = convMatch[3];
      var currency = convMatch[4].toLowerCase();

      if (currency === 'btc') {
        usd    = amount * price;
        amount *= 1000000;
        if (modifier === 'k' || modifier === 'K') {
          amount *= 1000;
          usd *= 1000;
        }
        self.client.doSay(conv + ' is ' + amount + ' Bit ' + usd + ' USD');
      } else {
        usd = amount * price;
        if (modifier === 'k' || modifier === 'K') {
          amount /= 1000;
          usd /= 1000;
        }
        else {
          amount /= 1000000;
          usd /= 1000000;
        }
        amount = amount.toFixed(8);
        amount = amount.replace(/\.0*$|0*$/,'');
        self.client.doSay(conv + ' is ' + amount + ' BTC ' + usd + ' USD');
      }
    });
  } else {
    self.client.doSay('usage: !convert <number>k? (btc|bit[s])');
  }
};

Shiba.prototype.onCmdBlock = function() {
  var self = this;
  Db.get('block', function(err, block) {
    if (err) return self.client.doSay('wow. such leveldb fail');

    var time     = new Date(block.time * 1000);
    var diff     = Date.now() - time;

    var line = 'Seen block #' + block.height;
    if (diff < 1000) {
      line += ' just now.';
    } else {
      line += ' ';
      line += Lib.formatTimeDiff(diff)
      line += ' ago.';
    }
    self.client.doSay(line);
  });
};

var shiba = new Shiba();
<commit_msg>Case-insensitive commands<commit_after>var fs           =  require('fs');
var async        =  require('async');
var profanity    =  require('./profanity');
var Bitstamp     =  require('./Bitstamp');
var Blockchain   =  require('./Blockchain');

var Client       =  require('./Client');
var Lib          =  require('./Lib');
var Db           =  require('./Db');
var Config       =  require('./Config')();

// Command syntax
var cmdReg = /^!([a-zA-z]*)\s*(.*)$/i;

function Shiba() {
  this.client = new Client(Config);

  this.setupUsernameScrape();
  this.setupConsoleLog();
  this.setupLossStreakComment();
  this.setupScamComment();
  this.setupBlockchain();
}

Shiba.prototype.setupUsernameScrape = function() {
  var self = this;
  self.client.on('player_bet', function(data) {
    Db.putUsername(data.username);
  });
  self.client.on('msg', function(msg) {
    if (msg.type != 'say') return;
    Db.putUsername(msg.username);
    Db.updateSeen(msg.username, msg);
    self.onSay(msg);
  });
};

Shiba.prototype.setupConsoleLog = function() {
  var self = this;
  self.client.on('game_starting', function(info) {
    var line = "Starting " + info.game_id
        + " " + info.hash.substring(0,8);
    process.stdout.write(line);
  });

  self.client.on('game_started', function(data) {
    process.stdout.write(".. ");
  });

  self.client.on('game_crash', function(data) {
    var gameInfo = self.client.getGameInfo();
    var crash = (data.game_crash/100).toFixed(2)
    process.stdout.write(" @" + crash + "x " + gameInfo.verified + "\n");
  });
};

Shiba.prototype.setupLossStreakComment = function() {
  var self = this;
  self.client.on('game_crash', function(data) {
    var gameHistory = self.client.gameHistory;

    // Determine loss streak
    var streak = gameHistory.length > 3;
    for (var i = 0; i < Math.min(gameHistory.length - 1, 4); ++i)
      streak = streak && gameHistory[i].game_crash <= 130;

    // Don't repeat yourself in a streak
    streak = streak && (gameHistory.length < 4 ||
                        gameHistory[i + 1].game_crash > 130);

    if (streak) self.client.doSay("wow. such rape. very butthurt");
  });
};

Shiba.prototype.setupScamComment = function() {
  var self = this;
  self.client.on('game_crash', function(data) {
    var gameInfo = self.client.getGameInfo();
    console.assert(gameInfo.hasOwnProperty('verified'));
    if (gameInfo.verified != 'ok') {
      self.client.doSay('wow. such scam. very hash failure.');
    }
  });
};

Shiba.prototype.setupBlockchain = function() {
  var bc = new Blockchain();

  bc.on('block', function(block) {
    Db.put('block', block);
  });
};

Shiba.prototype.getChatMessages = function(username, after) {
  var messages = [];
  var chatHistory = this.client.chatHistory;
  for (var i = 0; i < chatHistory.length; ++i) {
    then = new Date(chatHistory[i].time);
    if (then < after) break;

    if (chatHistory[i].username == username)
      messages.push(chatHistory[i]);
  }
  return messages;
};

Shiba.prototype.onSay = function(msg) {
  if (msg.username == this.client.username) return;

  // Coinurl blocking
  var coinurlReg = /http:\/\/cur\.lv\/.*/i;
  var coinurlMatch = msg.message.match(coinurlReg);
  if (coinurlMatch) return this.client.doMute(msg.username, '6h');

  // Minefield blocking
  var minefieldReg = /minefield\.bitcoinlab\.org\/.*(\?|&)r=/i;
  var minefieldMatch = msg.message.match(minefieldReg);
  if (minefieldMatch) return this.client.doMute(msg.username, '6h');

  // Cloud mining
  var cloudmineReg = /cloud.*mining.*http:\/\/vk\.cc\/[0-9a-z]/i;
  var cloudmineMatch = msg.message.match(minefieldReg);
  if (cloudmineMatch) return this.client.doMute(msg.username, '6h');

  var after, messages;
  // Rate limiter 5 messages in 5s
  after    = new Date(Date.now() - 5000);
  messages = this.getChatMessages(msg.username, after);
  if (messages.length >= 6) return this.client.doMute(msg.username, '15m');

  // Rate limiter 8 messages in 12s
  after    = new Date(Date.now() - 12000);
  messages = this.getChatMessages(msg.username, after);
  if (messages.length >= 9) return this.client.doMute(msg.username, '15m');

  var cmdMatch = msg.message.match(cmdReg);
  if (cmdMatch) this.onCmd(msg, cmdMatch[1], cmdMatch[2]);
};

Shiba.prototype.onCmd = function(msg, cmd, rest) {
  // Cmd rate limiter
  var after    = new Date(Date.now() - 10000);
  var messages = this.getChatMessages(msg.username, after);
  var rate = 0;

  for (var i = 0; i < messages.length; ++i)
    if (messages[i].message.match(cmdReg)) ++rate;

  if (rate >= 4)
    return this.client.doMute(msg.username, '5m');
  else if (rate >= 3)
    return this.client.doSay('bites ' + msg.username);

  switch(cmd.toLowerCase()) {
  case 'custom': this.onCmdCustom(msg, rest); break;
  case 'lick': this.onCmdLick(msg, rest); break;
  case 'seen': this.onCmdSeen(msg, rest); break;
  case 'convert': this.onCmdConvert(msg, rest); break;
  case 'block': this.onCmdBlock(msg, rest); break;
  }
};

Shiba.prototype.onCmdCustom = function(msg, rest) {
  var self = this;
  if (msg.role != 'admin' &&
      msg.role != 'moderator') return;

  var customReg   = /^([A-Za-z0-9]*)\s*(.*)$/;
  var customMatch = rest.match(customReg);
  var customUser  = customMatch[1];
  var customMsg   = customMatch[2];
  Db.addCustomLickMessage(customUser, customMsg, function (err) {
    if (err) {
      console.log('onCmdCustom:', err);
      self.client.doSay('wow. such leveldb fail');
    } else {
      self.client.doSay('wow. so cool. very obedient');
    }
  });
};

Shiba.prototype.onCmdLick = function(msg, user) {
  var self = this;
  user = user.toLowerCase();
  user = user.replace(/^\s+|\s+$/g,'');

  // We're cultivated and don't lick ourselves.
  if (user === self.client.username.toLowerCase()) return;

  if (profanity[user]) {
    self.client.doSay('so trollol. very annoying. such mute');
    self.client.doMute(msg.username, '5m');
    return;
  }

  async.parallel(
    [ function(cb) { Db.getUsername(user, cb); },
      function(cb) { Db.getCustomLickMessages(user, cb); }
    ],
    function (err, val) {
      if (err) return;

      var username = val[0];
      var customs  = val[1];
      customs.push('licks ' + username);
      var r = Math.random() * (customs.length - 0.8);
      var m = customs[Math.floor(r)];
      self.client.doSay(m);
    });
};

Shiba.prototype.onCmdSeen = function(msg, user) {
  var self = this;
  user = user.toLowerCase();
  user = user.replace(/^\s+|\s+$/g,'');

  // Special treatment of block.
  if (user === 'block') return this.onCmdBlock();

  // Avoid this.
  if (user === self.client.username.toLowerCase()) return;

  if (profanity[user]) {
    self.client.doSay('so trollol. very annoying. such mute');
    self.client.doMute(msg.username, '5m');
    return;
  }

  async.parallel(
    [ function(cb) { Db.getUsername(user, cb); },
      function(cb) { Db.getSeen(user, cb); }
    ],
    function (err, val) {
      if (err) return;

      var username = val[0];
      var msg      = val[1];
      var time     = new Date(msg.time);
      var diff     = Date.now() - time;

      var line;
      if (diff < 1000) {
        line = 'Seen ' + username + ' just now.';
      } else {
        line = 'Seen ' + username + ' ';
        line += Lib.formatTimeDiff(diff)
        line += ' ago.';
      }

      self.client.doSay(line);
    });
};

Shiba.prototype.onCmdConvert = function(msg, conv) {
  var self = this;
  conv = conv.replace(/^\s+|\s+$/g,'');
  var convReg = /^((-|\+)?[0-9]*\.?[0-9]*)(k?)\s*(bits?|btc)$/i;
  var convMatch = conv.match(convReg);

  if (convMatch) {
    Bitstamp.getAveragePrice(function(err, price) {

      console.log(convMatch);
      var amount   = parseFloat(convMatch[1], 10);
      var modifier = convMatch[3];
      var currency = convMatch[4].toLowerCase();

      if (currency === 'btc') {
        usd    = amount * price;
        amount *= 1000000;
        if (modifier === 'k' || modifier === 'K') {
          amount *= 1000;
          usd *= 1000;
        }
        self.client.doSay(conv + ' is ' + amount + ' Bit ' + usd + ' USD');
      } else {
        usd = amount * price;
        if (modifier === 'k' || modifier === 'K') {
          amount /= 1000;
          usd /= 1000;
        }
        else {
          amount /= 1000000;
          usd /= 1000000;
        }
        amount = amount.toFixed(8);
        amount = amount.replace(/\.0*$|0*$/,'');
        self.client.doSay(conv + ' is ' + amount + ' BTC ' + usd + ' USD');
      }
    });
  } else {
    self.client.doSay('usage: !convert <number>k? (btc|bit[s])');
  }
};

Shiba.prototype.onCmdBlock = function() {
  var self = this;
  Db.get('block', function(err, block) {
    if (err) return self.client.doSay('wow. such leveldb fail');

    var time     = new Date(block.time * 1000);
    var diff     = Date.now() - time;

    var line = 'Seen block #' + block.height;
    if (diff < 1000) {
      line += ' just now.';
    } else {
      line += ' ';
      line += Lib.formatTimeDiff(diff)
      line += ' ago.';
    }
    self.client.doSay(line);
  });
};

var shiba = new Shiba();
<|endoftext|>


<commit_before>var doc = document,
    qs  = doc.querySelector;

// -- Sulaco -------------------------------------------------------------------
var Sulaco;

Sulaco = {
    lineCache: {},
    playbackMode: false,

    coalesceMessages: function (lineNum) {
        var lineEl     = Sulaco.getLineEl(lineNum),
            prevLineEl = lineEl && Sulaco.getLineEl(lineEl.previousElementSibling),
            prevSender = Sulaco.getSenderNick(prevLineEl),
            sender     = Sulaco.getSenderNick(lineEl);

        if (!sender || !prevSender) {
            return;
        }

        if (sender === prevSender
                && Sulaco.getLineType(lineEl) === 'privmsg'
                && Sulaco.getLineType(prevLineEl) === 'privmsg') {

            lineEl.classList.add('coalesced');
            Sulaco.getSenderEl(lineEl).innerHTML = '';
        }
    },

    getLineEl: function (lineNum) {
        if (typeof lineNum === 'string') {
            return doc.getElementById('line' + lineNum);
        }

        if (lineNum && lineNum.classList
                && lineNum.classList.contains('line')) {
            return lineNum;
        }

        return null;
    },

    getLineType: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.getAttribute('type') : null;
    },

    getMessage: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.querySelector('.message').textContent.trim() : null;
    },

    getSenderEl: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.querySelector('.sender') : null;
    },

    getSenderNick: function (line) {
        var senderEl = Sulaco.getSenderEl(line);
        return senderEl ? senderEl.getAttribute('nick') : null;
    },

    handleBufferPlayback: function (lineNum) {
        var line = Sulaco.getLineEl(lineNum),
            message;

        if (Sulaco.getSenderNick(line) === '***') {
            message = Sulaco.getMessage(line);

            if (message === 'Buffer Playback...') {
                line.classList.add('znc-playback-start');
                Sulaco.playbackMode = true;
            } else if (message === 'Playback Complete.') {
                line.classList.add('znc-playback-end');
                Sulaco.playbackMode = false;
            }

            return;
        }

        if (Sulaco.playbackMode) {
            var match;

            line.classList.add('znc-playback');

            message = Sulaco.getMessage(line);
            match   = message.match(/^\[(\d\d:\d\d:\d\d)\] /);

            if (match) {
                var msgEl = line.querySelector('.message');

                line.querySelector('.time').textContent = match[1];
                msgEl.innerHTML = msgEl.innerHTML.replace(/^\s*\[\d\d:\d\d:\d\d\]/, '');
            }
        }
    }
};

// -- Textual ------------------------------------------------------------------

// Defined in: "Textual.app -> Contents -> Resources -> JavaScript -> API -> core.js"

Textual.newMessagePostedToView = function (lineNum) {
    Sulaco.handleBufferPlayback(lineNum);
    Sulaco.coalesceMessages(lineNum);
};

Textual.viewFinishedLoading = function () {
    Textual.fadeInLoadingScreen(1.00, 0.95);

    setTimeout(function () {
        Textual.scrollToBottomOfView();
    }, 300);
};

Textual.viewFinishedReload = function () {
    Textual.viewFinishedLoading();
};
<commit_msg>set more descriptive class names on certain lines<commit_after>var doc = document,
    qs  = doc.querySelector;

// -- Sulaco -------------------------------------------------------------------
var Sulaco;

Sulaco = {
    lineCache: {},
    playbackMode: false,

    coalesceMessages: function (lineNum) {
        var lineEl     = Sulaco.getLineEl(lineNum),
            prevLineEl = lineEl && Sulaco.getLineEl(lineEl.previousElementSibling),
            prevSender = Sulaco.getSenderNick(prevLineEl),
            sender     = Sulaco.getSenderNick(lineEl);

        if (!sender || !prevSender) {
            return;
        }

        if (sender === prevSender
                && Sulaco.getLineType(lineEl) === 'privmsg'
                && Sulaco.getLineType(prevLineEl) === 'privmsg') {

            lineEl.classList.add('coalesced');
            Sulaco.getSenderEl(lineEl).innerHTML = '';
        }
    },

    getLineEl: function (lineNum) {
        if (typeof lineNum === 'string') {
            return doc.getElementById('line' + lineNum);
        }

        if (lineNum && lineNum.classList
                && lineNum.classList.contains('line')) {
            return lineNum;
        }

        return null;
    },

    getLineType: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.getAttribute('type') : null;
    },

    getMessage: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.querySelector('.message').textContent.trim() : null;
    },

    getSenderEl: function (line) {
        line = Sulaco.getLineEl(line);
        return line ? line.querySelector('.sender') : null;
    },

    getSenderNick: function (line) {
        var senderEl = Sulaco.getSenderEl(line);
        return senderEl ? senderEl.getAttribute('nick') : null;
    },

    handleBufferPlayback: function (lineNum) {
        var line = Sulaco.getLineEl(lineNum),
            message;

        if (Sulaco.getSenderNick(line) === '***') {
            message = Sulaco.getMessage(line);

            if (message === 'Buffer Playback...') {
                line.classList.add('znc-playback-start');
                Sulaco.playbackMode = true;
            } else if (message === 'Playback Complete.') {
                line.classList.add('znc-playback-end');
                Sulaco.playbackMode = false;
            }

            return;
        }

        if (Sulaco.playbackMode) {
            var match;

            line.classList.add('znc-playback');

            message = Sulaco.getMessage(line);
            match   = message.match(/^\[(\d\d:\d\d:\d\d)\] /);

            if (match) {
                var msgEl = line.querySelector('.message');

                line.querySelector('.time').textContent = match[1];
                msgEl.innerHTML = msgEl.innerHTML.replace(/^\s*\[\d\d:\d\d:\d\d\]/, '');
            }
        }
    },

    setDescriptiveClassNames: function (lineNum) {
      var line = Sulaco.getLineEl(lineNum);

      if (line.getAttribute('type') === 'debug' &&
          /Disconnect/.test(line.textContent))
        line.classList.add('disconnect');

      if (line.getAttribute('type') === 'mode')
        line.classList.add('mode');

      if (line.getAttribute('type') === 'topic')
        line.classList.add('topic');

      if (line.getAttribute('type') === 'join' &&
          /aaronj1335/i.test(line.textContent))
        line.classList.add('aaronj1335-join');

      if (line.getAttribute('type') === 'quit' &&
          /query/i.test(line.textContent))
        line.classList.add('quit-query');

      if (line.getAttribute('type') === 'join' &&
          /query/i.test(line.textContent))
        line.classList.add('join-query');

    }
};

// -- Textual ------------------------------------------------------------------

// Defined in: "Textual.app -> Contents -> Resources -> JavaScript -> API -> core.js"

Textual.newMessagePostedToView = function (lineNum) {
    Sulaco.handleBufferPlayback(lineNum);
    Sulaco.coalesceMessages(lineNum);
    Sulaco.setDescriptiveClassNames(lineNum);
};

Textual.viewFinishedLoading = function () {
    Textual.fadeInLoadingScreen(1.00, 0.95);

    setTimeout(function () {
        Textual.scrollToBottomOfView();
    }, 300);
};

Textual.viewFinishedReload = function () {
    Textual.viewFinishedLoading();
};
<|endoftext|>


<commit_before>var express = require('express')
  , async   = require('async')
  , org     = require('./org-controller')
  , user    = require('../../users/controllers/user-controller')
  , db      = require(root + '/app/models');

module.exports = {

    findAll: function(request, response){
        db.job.findAll({
            include : [
                db.job_type
              , db.category
              , db.org
            ]
        }).then(function(jobs){
            response.json(jobs);
        });
    }

    , findById: function(request, response){
        var id = request.params.id;
        db.job.find({
            where   : { id : id }
          , include : [
              db.job_type
            , db.category
            , db.org
          ]
        }).then(function(job){
            response.json(job);
        });
    }

    , create: function(request, response){

        return db.job.create(request.body)
        .then(function(job){
            response.json(job);
        }).catch(function(error){
            response.json(400, error);
        });
    }

    /**
     * Invite an user and insert an organizations and a job.
     */
    , post : function(request, response){
        //begin transaction
        return db.sequelize.transaction(function(t){

            //invite an user
            return user.invite(request, response, t)
            .then(function(user){
                //create an organization
                request.body.USER_ID = user.id;
                //removes ambiguity between organization.name and user.name
                request.body.NAME = request.body.ORG_NAME;
                return db.org.create(request.body,{ transaction : t});
            })
            .then(function(org){
                //create a job
                request.body.ORG_ID = org.id;
                return db.job.create(request.body, { transaction : t})
            }).catch(function(err){
                response.json(400, err);
                //throw an error to rollback
                throw new Error(err);
            });

        }).then(function(result){
            //Transaction commited
            user.forgotPassword(request, response);
            return response.json(result);
        }).catch(function(err){
            //Trasaction rollbacked
            response.json(400, err);
        });
    }
};
<commit_msg>removed async library<commit_after>var express = require('express')
  , org     = require('./org-controller')
  , user    = require('../../users/controllers/user-controller')
  , db      = require(root + '/app/models');

module.exports = {

    findAll: function(request, response){
        db.job.findAll({
            include : [
                db.job_type
              , db.category
              , db.org
            ]
        }).then(function(jobs){
            response.json(jobs);
        });
    }

    , findById: function(request, response){
        var id = request.params.id;
        db.job.find({
            where   : { id : id }
          , include : [
              db.job_type
            , db.category
            , db.org
          ]
        }).then(function(job){
            response.json(job);
        });
    }

    , create: function(request, response){

        return db.job.create(request.body)
        .then(function(job){
            response.json(job);
        }).catch(function(error){
            response.json(400, error);
        });
    }

    /**
     * Invite an user and insert an organizations and a job.
     */
    , post : function(request, response){
        //begin transaction
        return db.sequelize.transaction(function(t){

            //invite an user
            return user.invite(request, response, t)
            .then(function(user){
                //create an organization
                request.body.USER_ID = user.id;
                //removes ambiguity between organization.name and user.name
                request.body.NAME = request.body.ORG_NAME;
                return db.org.create(request.body,{ transaction : t});
            })
            .then(function(org){
                //create a job
                request.body.ORG_ID = org.id;
                return db.job.create(request.body, { transaction : t})
            }).catch(function(err){
                response.json(400, err);
                //throw an error to rollback
                throw new Error(err);
            });

        }).then(function(result){
            //Transaction commited
            user.forgotPassword(request, response);
            return response.json(result);
        }).catch(function(err){
            //Trasaction rollbacked
            response.json(400, err);
        });
    }
};
<|endoftext|>


<commit_before>//binding the enter key to the input

var $input = $('#new-todo');

$input.keyup(function(e){
  if(e.keyCode == 13)  {
    $(this).trigger("addTask");
    }
  });

/* ensure there is more than whitespace in the input field
// If yes, then
// display the main section
// add the input field value to the list item
// clear the input value
*/
$input.bind("addTask",function(){
  if ($input.val().trim().length > 0 ){
    $('#main').css({display: 'block'});
    $('#todo-list').show()
      .append('

<input class="toggle" type="checkbox">' +
      $input.val() + '<button class="destroy"></button>
<input class="edit" value="' +
      $input.val() + '" >
');

    //Clears the input field
    $input.val('');

    // Display the footer
    $('#footer').show();

    // update footer new count
    $('#todo-count strong').text($('#todo-list li').length);

  }
});

/*
//
// TODO: Add the :before icon button when creating the

//       Create the click functionality for the button to fire an event
//       #todo-list > li should be listening for an event to fire and toggle class
//       #toggle-all icon needs click functionality to toggle class of all

*/
 var $complete = $('#todo-list li');
 var $toggleAll = $('#toggle-all');

 $toggleAll.click(function() {
   if ($('#todo-list li').hasClass('completed')) {
     $('#todo-list li').removeClass('completed');
   } else {
     $('#todo-list li').addClass('completed');
   }
 });

var $toggle = $('.toggle');

$('#todo-list').on('click', '.toggle', function(){
  $(this).closest('li').toggleClass('completed');
});

// $('#todo-list li').addClass('completed)
<commit_msg>finished checkboxes and completeTD feature<commit_after>//binding the enter key to the input

var $input = $('#new-todo');

$input.keyup(function(e){
  if(e.keyCode == 13)  {
    $(this).trigger("addTask");
    }
  });

/* ensure there is more than whitespace in the input field
// If yes, then
// display the main section
// add the input field value to the list item
// clear the input value
*/
$input.bind("addTask",function(){
  if ($input.val().trim().length > 0 ){
    $('#main').css({display: 'block'});
    $('#todo-list').show()
      .append('

<input class="toggle" type="checkbox">' +
      $input.val() + '<button class="destroy"></button>
<input class="edit" value="' +
      $input.val() + '" >
');

    //Clears the input field
    $input.val('');

    // Display the footer
    $('#footer').show();

    // update footer new count
    $('#todo-count strong').text($('#todo-list li').length);

  }
});

/*
//
// TODO: Add the :before icon button when creating the

//       Create the click functionality for the button to fire an event
//       #todo-list > li should be listening for an event to fire and toggle class
//       #toggle-all icon needs click functionality to toggle class of all

*/
 var $complete = $('#todo-list li');
 var $toggleAll = $('#toggle-all');

 $toggleAll.click(function() {
   if ($('#todo-list li').hasClass('')) {
     $('#todo-list li').addClass('completed');
     $('.toggle').prop('checked', true);
   } else {
     $('.toggle').prop('checked', false);
     $('#todo-list li').removeClass('completed');
   }
 });

$('#todo-list').on('click', '.toggle', function(){
  $(this).closest('li').toggleClass('completed');
  if($('.completed').length === $('#todo-list li').length) {
    $('#toggle-all').prop('checked', true);
  } else {
    $('#toggle-all').prop('checked', false);
  }
});

// $('#todo-list li').addClass('completed)
<|endoftext|>


<commit_before>import { mapGetters } from 'vuex';

const typeMap = {
  0: 'Skill-Up Crest',
  1: 'Weapon',
  8: 'Necklace',
  9: 'Earrings',
  10: 'Ring',
};

const indexMap = [typeMap[1], typeMap[8], typeMap[9], typeMap[10], typeMap[10]];

export default {
  computed: {
    ...mapGetters([
      'techCount',
    ]),
  },

  methods: {
    techLevel(skillId, index) {
      const skills = this.skills;
      const skill = skills[skillId];
      const name = indexMap[index];
      const tech = skill.techs.filter(t => typeMap[t.type] === name)[0];

      return tech.level;
    },
  },

  filters: {
    typeName(type) {
      return typeMap[type];
    },

    indexName(index) {
      return indexMap[index];
    },
  },
};
<commit_msg>resolves #63 and #64 - not sure what else I can remove now...<commit_after>const typeMap = {
  0: 'Skill-Up Crest',
  1: 'Weapon',
  8: 'Necklace',
  9: 'Earrings',
  10: 'Ring',
};

const indexMap = [typeMap[1], typeMap[8], typeMap[9], typeMap[10], typeMap[10]];

export default {
  methods: {
    techLevel(skillId, index) {
      const skills = this.skills;
      const skill = skills[skillId];
      const name = indexMap[index];
      const tech = skill.techs.filter(t => typeMap[t.type] === name)[0];

      return tech.level;
    },
  },

  filters: {
    typeName(type) {
      return typeMap[type];
    },

    indexName(index) {
      return indexMap[index];
    },
  },
};
<|endoftext|>


<commit_before>import Posts from '../collection.js'
import Users from 'meteor/vulcan:users';
import { addCallback, getSetting } from 'meteor/vulcan:core';
import Events from 'meteor/vulcan:events';

// ------------------------------------- posts.remove.sync -------------------------------- //

function PostsRemoveOperations (post) {
  Users.update({_id: post.userId}, {$inc: {"postCount": -1}});
  return post;
}
addCallback("posts.remove.sync", PostsRemoveOperations);

// ------------------------------------- posts.approve.async -------------------------------- //

/**
 * @summary set postedAt when a post is approved
 */
function PostsSetPostedAt (modifier, post) {
  modifier.$set.postedAt = new Date();
  if (modifier.$unset) {
    delete modifier.$unset.postedAt;
  }
  return modifier;
}
addCallback("posts.approve.sync", PostsSetPostedAt);

// ------------------------------------- users.remove.async -------------------------------- //

function UsersRemoveDeletePosts (user, options) {
  if (options.deletePosts) {
    Posts.remove({userId: user._id});
  } else {
    // not sure if anything should be done in that scenario yet
    // Posts.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true});
  }
}
addCallback("users.remove.async", UsersRemoveDeletePosts);

// /**
//  * @summary Increase the number of clicks on a post
//  * @param {string} postId – the ID of the post being edited
//  * @param {string} ip – the IP of the current user
//  */
Posts.increaseClicks = (post, ip) => {
  const clickEvent = {
    name: 'click',
    properties: {
      postId: post._id,
      ip: ip
    }
  };

  if (getSetting('trackClickEvents', true)) {
    // make sure this IP hasn't previously clicked on this post
    const existingClickEvent = Events.findOne({name: 'click', 'properties.postId': post._id, 'properties.ip': ip});

    if(!existingClickEvent) {
      Events.log(clickEvent);
      return Posts.update(post._id, { $inc: { clickCount: 1 }});
    }
  } else {
    return Posts.update(post._id, { $inc: { clickCount: 1 }});
  }
};

function PostsClickTracking(post, ip) {
  return Posts.increaseClicks(post, ip);
}

// track links clicked, locally in Events collection
// note: this event is not sent to segment cause we cannot access the current user
// in our server-side route /out -> sending an event would create a new anonymous
// user: the free limit of 1,000 unique users per month would be reached quickly
addCallback('posts.click.async', PostsClickTracking);<commit_msg>fix postedAt callback<commit_after>import Posts from '../collection.js'
import Users from 'meteor/vulcan:users';
import { addCallback, getSetting } from 'meteor/vulcan:core';
import Events from 'meteor/vulcan:events';

// ------------------------------------- posts.remove.sync -------------------------------- //

function PostsRemoveOperations (post) {
  Users.update({_id: post.userId}, {$inc: {"postCount": -1}});
  return post;
}
addCallback("posts.remove.sync", PostsRemoveOperations);

// ------------------------------------- posts.approve.async -------------------------------- //

/**
 * @summary set postedAt when a post is approved and it doesn't have a postedAt date
 */
function PostsSetPostedAt (modifier, post) {
  if (!modifier.$set.postedAt && !post.postedAt) {
    modifier.$set.postedAt = new Date();
    if (modifier.$unset) {
      delete modifier.$unset.postedAt;
    }
  }
  return modifier;
}
addCallback("posts.approve.sync", PostsSetPostedAt);

// ------------------------------------- users.remove.async -------------------------------- //

function UsersRemoveDeletePosts (user, options) {
  if (options.deletePosts) {
    Posts.remove({userId: user._id});
  } else {
    // not sure if anything should be done in that scenario yet
    // Posts.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true});
  }
}
addCallback("users.remove.async", UsersRemoveDeletePosts);

// /**
//  * @summary Increase the number of clicks on a post
//  * @param {string} postId – the ID of the post being edited
//  * @param {string} ip – the IP of the current user
//  */
Posts.increaseClicks = (post, ip) => {
  const clickEvent = {
    name: 'click',
    properties: {
      postId: post._id,
      ip: ip
    }
  };

  if (getSetting('trackClickEvents', true)) {
    // make sure this IP hasn't previously clicked on this post
    const existingClickEvent = Events.findOne({name: 'click', 'properties.postId': post._id, 'properties.ip': ip});

    if(!existingClickEvent) {
      Events.log(clickEvent);
      return Posts.update(post._id, { $inc: { clickCount: 1 }});
    }
  } else {
    return Posts.update(post._id, { $inc: { clickCount: 1 }});
  }
};

function PostsClickTracking(post, ip) {
  return Posts.increaseClicks(post, ip);
}

// track links clicked, locally in Events collection
// note: this event is not sent to segment cause we cannot access the current user
// in our server-side route /out -> sending an event would create a new anonymous
// user: the free limit of 1,000 unique users per month would be reached quickly
addCallback('posts.click.async', PostsClickTracking);<|endoftext|>


<commit_before>var Q = require('q'),
    s3 = require('gulp-s3'),
    git = require('git-rev'),
    gulp = require('gulp'),
    karma = require('gulp-karma'),
    gutil = require('gulp-util'),
    gzip = require("gulp-gzip"),
    brunch = require('brunch'),
    imagemin = require('gulp-imagemin'),
    runSequence = require('run-sequence'),
    environment = process.env.ENV || 'staging',
    aws = require('./aws-config')[environment];

gulp.task('build:dev', function() {
  var deferred = Q.defer();

  brunch.build({}, function() {
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('build:prod', function() {
  var deferred = Q.defer();

  brunch.build({production: true}, function() {
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('test:dev', ['build:dev'], function() {
  return gulp.src([
    'public/css/app.css',
    'public/js/app.js',
    'bower_components/handlebars/handlebars.js',
    'test/enviroment.js',
    'test/**/*-test.js'
  ]).pipe(karma({
    action: 'run',
    configFile: 'karma.conf.coffee'
  }));
});

gulp.task('test:prod', ['build:prod'], function() {
  return gulp.src([
    'public/css/app.css',
    'public/js/app.js',
    'bower_components/handlebars/handlebars.js',
    'test/enviroment.js',
    'test/**/*-test.js'
  ]).pipe(karma({
    action: 'run',
    browsers: ['Chrome', 'Safari', 'Firefox'],
    configFile: 'karma.conf.coffee'
  }));
});

gulp.task('deploy:js', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/js/*'
    ], {read: false})
    .pipe(gzip())
    .pipe(s3(aws, {uploadPath: '/' + hash + '/js/'}))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:css', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/css/*'
    ], {read: false})
    .pipe(gzip())
    .pipe(s3(aws, {uploadPath: '/' + hash + '/css/'}))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:images', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/images/**'
    ], {read: false})
    .pipe(imagemin())
    .pipe(gzip())
    .pipe(s3(aws, {uploadPath: '/' + hash + '/images/'}))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:summary', function() {
  var deferred = Q.defer();

  git.short(function(hash) {
    gutil.log('Environment:', gutil.colors.magenta(environment));
    gutil.log('Deploying to AWS bucket:', gutil.colors.magenta(aws.bucket));
    gutil.log('BOARDS_WEB_CLIENT_VERSION=' + gutil.colors.magenta(hash));
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('deploy', function(cb) {
  runSequence('deploy:summary', 'build:prod', 'deploy:js', 'deploy:css', 'deploy:images', cb);
});
<commit_msg>Add Cache-Control headers<commit_after>var _ = require('lodash'),
    Q = require('q'),
    s3 = require('gulp-s3'),
    git = require('git-rev'),
    gulp = require('gulp'),
    karma = require('gulp-karma'),
    gutil = require('gulp-util'),
    gzip = require("gulp-gzip"),
    brunch = require('brunch'),
    imagemin = require('gulp-imagemin'),
    runSequence = require('run-sequence'),
    environment = process.env.ENV || 'staging',
    aws = require('./aws-config')[environment];

var s3Options = function(options) {
  options = options || {};

  return _.merge(options,{
    gzippedOnly: true,
    headers: {
      'Cache-Control': 'max-age=315360000, no-transform, public'
    }
  });
};

gulp.task('build:dev', function() {
  var deferred = Q.defer();

  brunch.build({}, function() {
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('build:prod', function() {
  var deferred = Q.defer();

  brunch.build({production: true}, function() {
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('test:dev', ['build:dev'], function() {
  return gulp.src([
    'public/css/app.css',
    'public/js/app.js',
    'bower_components/handlebars/handlebars.js',
    'test/enviroment.js',
    'test/**/*-test.js'
  ]).pipe(karma({
    action: 'run',
    configFile: 'karma.conf.coffee'
  }));
});

gulp.task('test:prod', ['build:prod'], function() {
  return gulp.src([
    'public/css/app.css',
    'public/js/app.js',
    'bower_components/handlebars/handlebars.js',
    'test/enviroment.js',
    'test/**/*-test.js'
  ]).pipe(karma({
    action: 'run',
    browsers: ['Chrome', 'Safari', 'Firefox'],
    configFile: 'karma.conf.coffee'
  }));
});

gulp.task('deploy:js', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/js/*'
    ], {read: false})
    .pipe(gzip())
    .pipe(s3(aws, s3Options({uploadPath: '/' + hash + '/js/'})))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:css', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/css/*'
    ], {read: false})
    .pipe(gzip())
    .pipe(s3(aws, s3Options({uploadPath: '/' + hash + '/css/'})))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:images', function() {
  var deferred = Q.defer();

  git.short(function (hash) {
    gulp.src([
      'public/images/**'
    ], {read: false})
    .pipe(imagemin())
    .pipe(gzip())
    .pipe(s3(aws, s3Options({uploadPath: '/' + hash + '/images/'})))
    .on('close', function() {
      deferred.resolve();
    });
  });

  return deferred.promise;
});

gulp.task('deploy:summary', function() {
  var deferred = Q.defer();

  git.short(function(hash) {
    gutil.log('Environment:', gutil.colors.magenta(environment));
    gutil.log('Deploying to AWS bucket:', gutil.colors.magenta(aws.bucket));
    gutil.log('BOARDS_WEB_CLIENT_VERSION=' + gutil.colors.magenta(hash));
    deferred.resolve();
  });

  return deferred.promise;
});

gulp.task('deploy', function(cb) {
  runSequence('deploy:summary', 'build:prod', 'deploy:js', 'deploy:css', 'deploy:images', cb);
});
<|endoftext|>


<commit_before>'use strict';

const assert  = require('chai').assert;
const express = require('express');
const path    = require('path');
const _       = require('lodash');
const navit   = require('../');
const helpers = require('./helpers');

const ENGINE = process.env.ENGINE;

describe('Navit api', function () {
  this.timeout(100000);
  let server;
  let browser;

  before(function (done) {
    browser = navit({ prefix: 'http://localhost:17345', engine: ENGINE });

    server = express()
      .use(express.static(path.join(__dirname, '..')))
      .listen(17345, err => {
        if (err) return done(err);
        // Init engine before execute first test
        browser.run(done);
      });
  });

  describe('.registerMethod', function () {
    it('should register function in one namespace', function () {
      var n = navit();

      n.registerMethod('test123', () => 'val');

      assert.equal(n.test123(), 'val');
    });

    it('should remove function from one namespace', function () {
      var n = navit();

      n.registerMethod('test123', () => 'val');

      // Remove
      n.registerMethod('test123');

      assert.throws(() => n.test123(), TypeError);
    });

    it('should register function in multi namespace', function () {
      var n = navit();

      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ], function () {
        return 'val';
      });

      assert.equal(n.my.test.namespace.one.test123(), 'val');
      assert.equal(n.my.test.namespace.two.test123(), 'val');
    });

    it('should remove function from multi namespace', function () {
      let n = navit();

      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ], function () {
        return 'val';
      });

      // Remove
      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ]);

      assert.throws(() => n.my.test.namespace.one.test123(), TypeError);
      assert.throws(() => n.my.test.namespace.two.test123(), TypeError);
    });

    it('registered function should work in correct context', function () {
      let n = navit();

      n.testProperty = 'test value';

      n.registerMethod('test123', function () {
        return this.testProperty;
      });

      assert.equal(n.test123(), 'test value');
    });

    it('should register chain as function', function () {
      var n = navit();

      n.registerMethod('test123',     () => '123');
      n.registerMethod('test123.not', () => 'not123');

      assert.equal(n.test123(), '123');
      assert.equal(n.test123.not(), 'not123');
    });

    it('should register remove function without', function () {
      var n = navit();

      n.registerMethod('test123',     () => '123');
      n.registerMethod('test123.not', () => 'not123');

      // Remove
      n.registerMethod('test123');

      assert.throws(() => n.test123(), TypeError);
      assert.equal(n.test123.not(), 'not123');
    });
  });

  it('.batch', function () {
    browser.batch.create('test_batch', function () {
      this
        .open('/test/fixtures/api/batch.html')
        .set.cookie('batch', 'cookies');
    });

    return browser
      .batch('test_batch')
      .get.cookies(val => assert.equal(val[0].value, 'cookies'));
  });

  describe('.fn', function () {
    it('async', function () {
      let results = [];

      return browser
        .open('/test/fixtures/api/fn.html')
        .get.text('body', results)
        .fn((a, b, c, next) => {
          // Need trim because SlimerJS can add new line symbols at start and at end of body
          assert.equal(_.trim(results[0], '\n'), 'test text');
          assert.equal(a, 'a');
          assert.equal(b, 'b');
          assert.equal(c, 'c');

          next();
        }, 'a', 'b', 'c');
    });

    it('sync', function () {
      var results = [];

      return browser
        .open('/test/fixtures/api/fn.html')
        .get.text('body', results)
        .fn((a, b, c) => {
          // Need trim because SlimerJS can add new line symbols at start and at end of body
          assert.equal(_.trim(results[0], '\n'), 'test text');
          assert.equal(a, 'a');
          assert.equal(b, 'b');
          assert.equal(c, 'c');
        }, 'a', 'b', 'c');
    });
  });

  it('.use', function () {
    browser
      .use(function (navit, a, b, c) {
        navit.registerMethod('use_test', () => a + b + c);
      }, 'a', 'b', 'c');

    assert.equal(browser.use_test(), 'abc');
  });

  it('afterOpen', function () {
    browser.afterOpen = function () {
      this
        .set.cookie('ao', 'ao-test-cookies')
        .do.type('#ao-test', 'ao-test-type');
    };

    return browser
      .open('/test/fixtures/api/after_open.html')
      .test.evaluate(function () {
        return document.querySelector('#ao-test').value === 'ao-test-type';
      })
      .get.cookies(cookies => {
        let cookie = _.find(cookies, cookie => cookie.name === 'ao');

        assert.equal(cookie.value, 'ao-test-cookies');
      })
      .then(
        () => { browser.afterOpen = null; },
        err => { browser.afterOpen = null; throw err; }
      );
  });

  it('options.inject', function () {
    // workaround for utf8 in dir names for SlimeerJS
    let file = helpers.toTmp(path.join(__dirname, 'fixtures', 'api', 'inject.js'));

    let browser = navit({ inject: [
      file
    ], engine: ENGINE });

    return browser
      .open('http://localhost:17345/test/fixtures/api/inject.html')
      .test.text('#html-from-js', 'html from js')
      .do.reload()
      .test.text('#html-from-js', 'html from js')
      .close()
      .then(
        () => { helpers.unlink(file); },
        err => { helpers.unlink(file); throw err; }
      );
  });

  it('engineOptions', function () {
    let browser = navit({ engine: ENGINE }, { loadImages: false });

    return browser
      .open('http://localhost:17345/test/fixtures/api/engine_options.html')
      .get.evaluate(function () {
        return [ document.querySelector('img').clientWidth, document.querySelector('img').clientHeight ];
      }, size => assert.notDeepEqual(size, [ 1, 1 ]))
      .close();
  });

  it('.close', function () {
    // check that server automatically restarts when you close it midway
    browser
      .close()
      .open('/test/fixtures/api/close.html')
      .test.evaluate(function () {
        return document.querySelector('#test').value === 'foobar';
      })
      .run();
  });

  describe('.run', function () {
    // regression test: check that callback is called on teardown
    it('teardown', function (callback) {
      browser
        .open('/test/fixtures/api/run.html')
        .run(true, callback);
    });

    it('no teardown', function (callback) {
      browser
        .open('/test/fixtures/api/run.html')
        .run(false, callback);
    });
  });

  after(function (done) {
    server.close();
    browser.exit(done);
  });
});
<commit_msg>Fix failing test<commit_after>'use strict';

const assert  = require('chai').assert;
const express = require('express');
const path    = require('path');
const _       = require('lodash');
const navit   = require('../');
const helpers = require('./helpers');

const ENGINE = process.env.ENGINE;

describe('Navit api', function () {
  this.timeout(100000);
  let server;
  let browser;

  before(function (done) {
    browser = navit({ prefix: 'http://localhost:17345', engine: ENGINE });

    server = express()
      .use(express.static(path.join(__dirname, '..')))
      .listen(17345, err => {
        if (err) return done(err);
        // Init engine before execute first test
        browser.run(done);
      });
  });

  describe('.registerMethod', function () {
    it('should register function in one namespace', function () {
      var n = navit();

      n.registerMethod('test123', () => 'val');

      assert.equal(n.test123(), 'val');
    });

    it('should remove function from one namespace', function () {
      var n = navit();

      n.registerMethod('test123', () => 'val');

      // Remove
      n.registerMethod('test123');

      assert.throws(() => n.test123(), TypeError);
    });

    it('should register function in multi namespace', function () {
      var n = navit();

      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ], function () {
        return 'val';
      });

      assert.equal(n.my.test.namespace.one.test123(), 'val');
      assert.equal(n.my.test.namespace.two.test123(), 'val');
    });

    it('should remove function from multi namespace', function () {
      let n = navit();

      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ], function () {
        return 'val';
      });

      // Remove
      n.registerMethod([ 'my.test.namespace.one.test123', 'my.test.namespace.two.test123' ]);

      assert.throws(() => n.my.test.namespace.one.test123(), TypeError);
      assert.throws(() => n.my.test.namespace.two.test123(), TypeError);
    });

    it('registered function should work in correct context', function () {
      let n = navit();

      n.testProperty = 'test value';

      n.registerMethod('test123', function () {
        return this.testProperty;
      });

      assert.equal(n.test123(), 'test value');
    });

    it('should register chain as function', function () {
      var n = navit();

      n.registerMethod('test123',     () => '123');
      n.registerMethod('test123.not', () => 'not123');

      assert.equal(n.test123(), '123');
      assert.equal(n.test123.not(), 'not123');
    });

    it('should register remove function without', function () {
      var n = navit();

      n.registerMethod('test123',     () => '123');
      n.registerMethod('test123.not', () => 'not123');

      // Remove
      n.registerMethod('test123');

      assert.throws(() => n.test123(), TypeError);
      assert.equal(n.test123.not(), 'not123');
    });
  });

  it('.batch', function () {
    browser.batch.create('test_batch', function () {
      this
        .open('/test/fixtures/api/batch.html')
        .set.cookie('batch', 'cookies');
    });

    return browser
      .batch('test_batch')
      .get.cookies(val => assert.equal(val[0].value, 'cookies'));
  });

  describe('.fn', function () {
    it('async', function () {
      let results = [];

      return browser
        .open('/test/fixtures/api/fn.html')
        .get.text('body', results)
        .fn((a, b, c, next) => {
          // Need trim because SlimerJS can add new line symbols at start and at end of body
          assert.equal(_.trim(results[0], '\n'), 'test text');
          assert.equal(a, 'a');
          assert.equal(b, 'b');
          assert.equal(c, 'c');

          next();
        }, 'a', 'b', 'c');
    });

    it('sync', function () {
      var results = [];

      return browser
        .open('/test/fixtures/api/fn.html')
        .get.text('body', results)
        .fn((a, b, c) => {
          // Need trim because SlimerJS can add new line symbols at start and at end of body
          assert.equal(_.trim(results[0], '\n'), 'test text');
          assert.equal(a, 'a');
          assert.equal(b, 'b');
          assert.equal(c, 'c');
        }, 'a', 'b', 'c');
    });
  });

  it('.use', function () {
    browser
      .use(function (navit, a, b, c) {
        navit.registerMethod('use_test', () => a + b + c);
      }, 'a', 'b', 'c');

    assert.equal(browser.use_test(), 'abc');
  });

  it('afterOpen', function () {
    browser.afterOpen = function () {
      this
        .set.cookie('ao', 'ao-test-cookies')
        .do.type('#ao-test', 'ao-test-type');
    };

    return browser
      .open('/test/fixtures/api/after_open.html')
      .test.evaluate(function () {
        return document.querySelector('#ao-test').value === 'ao-test-type';
      })
      .get.cookies(cookies => {
        let cookie = _.find(cookies, cookie => cookie.name === 'ao');

        assert.equal(cookie.value, 'ao-test-cookies');
      })
      .then(
        () => { browser.afterOpen = null; },
        err => { browser.afterOpen = null; throw err; }
      );
  });

  it('options.inject', function () {
    // workaround for utf8 in dir names for SlimeerJS
    let file = helpers.toTmp(path.join(__dirname, 'fixtures', 'api', 'inject.js'));

    let browser = navit({ inject: [
      file
    ], engine: ENGINE });

    return browser
      .open('http://localhost:17345/test/fixtures/api/inject.html')
      .test.text('#html-from-js', 'html from js')
      .do.reload()
      .test.text('#html-from-js', 'html from js')
      .close()
      .then(
        () => { helpers.unlink(file); },
        err => { helpers.unlink(file); throw err; }
      );
  });

  it('engineOptions', function () {
    let browser = navit({ engine: ENGINE }, { loadImages: false });

    return browser
      .open('http://localhost:17345/test/fixtures/api/engine_options.html')
      .get.evaluate(function () {
        return [ document.querySelector('img').clientWidth, document.querySelector('img').clientHeight ];
      }, size => assert.notDeepEqual(size, [ 1, 1 ]))
      .close();
  });

  it('.close', function () {
    // check that server automatically restarts when you close it midway
    return browser
      .close()
      .open('/test/fixtures/api/close.html')
      .test.evaluate(function () {
        return document.querySelector('#test').value === 'foobar';
      })
      .run();
  });

  describe('.run', function () {
    // regression test: check that callback is called on teardown
    it('teardown', function (callback) {
      browser
        .open('/test/fixtures/api/run.html')
        .run(true, callback);
    });

    it('no teardown', function (callback) {
      browser
        .open('/test/fixtures/api/run.html')
        .run(false, callback);
    });
  });

  after(function (done) {
    server.close();
    browser.exit(done);
  });
});
<|endoftext|>