<commit_before>var c = require('constants');

function countRoles(role) {
  return _.filter(Game.creeps, (creep) => creep.memory.role == role).length
}

function getRole() {
  if (countRoles('harvester') < 2) {
    return 'harvester';
  }
  // TODO: check if we are under attack
  // TODO: make sure there are construction sites
  return 'builder';
}

function getCost(body) {
  var cost = 0;
  for (piece in body) {
    cost += c.costs[body[piece]];
  }
  return cost;
}

var adds = [
  [WORK, CARRY, MOVE, MOVE],
  [WORK, MOVE],
  [CARRY, MOVE],
  [MOVE]
];

function getBody(role, energy) {
  var base = [WORK, CARRY, MOVE];
  var cost = getCost(base);

  while (cost < energy) {
    var remaining = energy - cost;
    var added = false;

    // add the largest chunk we can, bail if there isn't enough energy left
    for (var i in adds) {
      var toAdd = adds[i];
      if (getCost(toAdd) < remaining) {
        base.concat(toAdd);
        cost = getCost(base);
        added = true;
        break;
      }
    }

    if (!added) {
      break;
    }
  }
  return base;
}

function spawnCreep(spawn, energy) {
  var role = getRole();
  var body = getBody(role, energy);
  console.log('Spawning a ' + role + ' using ' + energy + ' energy ' + body);
  spawn.createCreep(body, undefined, {role: role});
}

function run() {
  for (var name in Game.spawns) {
    var spawn = Game.spawns[name];
    var room = spawn.room;
    // TODO: handle energy in multiple spawns?
    if (room.energyAvailable === room.energyCapacityAvailable) {
      spawnCreep(spawn, room.energyAvailable);
    }
  }
}

module.exports = {
  run: run
};
<commit_msg>still not right. unblock work<commit_after>var c = require('constants');

function countRoles(role) {
  return _.filter(Game.creeps, (creep) => creep.memory.role == role).length
}

function getRole() {
  if (countRoles('harvester') < 2) {
    return 'harvester';
  }
  // TODO: check if we are under attack
  // TODO: make sure there are construction sites
  return 'builder';
}

function getCost(body) {
  var cost = 0;
  for (piece in body) {
    cost += c.costs[body[piece]];
  }
  return cost;
}

var adds = [
  [WORK, CARRY, MOVE, MOVE],
  [WORK, MOVE],
  [CARRY, MOVE],
  [MOVE]
];

function getBody(role, energy) {
  var base = [WORK, CARRY, MOVE];
  var cost = getCost(base);

  while (cost < energy && false) {
    var remaining = energy - cost;
    var added = false;

    // add the largest chunk we can, bail if there isn't enough energy left
    for (var i in adds) {
      var toAdd = adds[i];
      if (getCost(toAdd) < remaining) {
        base.concat(toAdd);
        cost = getCost(base);
        added = true;
        break;
      }
    }

    if (!added) {
      break;
    }
  }
  return base;
}

function spawnCreep(spawn, energy) {
  var role = getRole();
  var body = getBody(role, energy);
  console.log('Spawning a ' + role + ' using ' + energy + ' energy ' + body);
  spawn.createCreep(body, undefined, {role: role});
}

function run() {
  for (var name in Game.spawns) {
    var spawn = Game.spawns[name];
    var room = spawn.room;
    // TODO: handle energy in multiple spawns?
    if (room.energyAvailable === room.energyCapacityAvailable) {
      spawnCreep(spawn, room.energyAvailable);
    }
  }
}

module.exports = {
  run: run
};
<|endoftext|>


<commit_before>define(function(require) {

    'use strict';

    var PIXI = require('pixi');
    var PixiView = require('../view');

    var Colors    = require('../../colors/colors');
    var Vector2   = require('../../math/vector2');
    var Rectangle = require('../../math/rectangle');

    var ThermometerView = PixiView.extend({

        initialize: function(options) {
            options = _.extend({
                bulbDiameter: 24,
                tubeWidth:    12,
                tubeHeight:   64,

                numberOfTicks: 7,

                lineColor: '#999',
                lineWidth: 1,
                lineAlpha: 1,

                fillColor: '#fff',
                fillAlpha: 0.3,

                liquidColor: '#ff3c00'
            }, options);

            this.bulbDiameter = options.bulbDiameter;
            this.tubeWidth = options.tubeWidth;
            this.tubeHeight = options.tubeHeight;

            this.lineColor = Colors.parseHex(options.lineColor);
            this.lineWidth = options.lineWidth;
            this.lineAlpha = options.lineAlpha;

            this.fillColor = Colors.parseHex(options.fillColor);
            this.fillAlpha = options.fillAlpha;

            this.liquidColor = Colors.parseHex(options.liquidColor);

            this.value = 1.2;

            this.padding = 2;
            this.outlineFix = 0.5;

            this.initGraphics();
        },

        initGraphics: function() {
            this.initShape();
            this.initLiquid();
            this.initTicks();
        },

        initShape: function() {
            var background = new PIXI.Graphics();
            var outline    = new PIXI.Graphics();

            // Only want to deal in whole pixels so it looks crisp
            var radius = Math.floor(this.bulbDiameter / 2);
            var halfWidth = Math.floor(this.tubeWidth / 2);
            var width = halfWidth * 2;
            var height = Math.floor(this.tubeHeight);

            // Filling it is easy.
            background.beginFill(this.fillColor, 1);
            background.drawCircle(0, 0, radius);
            background.drawRect(-halfWidth, 0, width, -radius - height);
            background.drawCircle(0, -radius - height, halfWidth);
            background.endFill();

            // (Except that applying an alpha to the graphics is actually buggy,
            //   so I'll create a texture and set an alpha on a sprite.)
            var bgSprite = new PIXI.Sprite(background.generateTexture());
            bgSprite.anchor.x = 0.5;
            bgSprite.anchor.y = (bgSprite.height - radius) / bgSprite.height;
            bgSprite.alpha = this.fillAlpha;

            // It's drawing the outline that's tricky.
            var theta = Math.acos(halfWidth / radius);
            var tubeBulbIntersectionY = -Math.sqrt(Math.pow(radius, 2) - Math.pow(halfWidth, 2));

            outline.lineStyle(this.lineWidth, this.lineColor, this.lineAlpha);
            outline.moveTo(-halfWidth, tubeBulbIntersectionY);
            outline.lineTo(-halfWidth, -radius - height);
            outline.arc(0, -radius - height, halfWidth, Math.PI, 0);
            outline.lineTo(halfWidth, tubeBulbIntersectionY);
            outline.arc(0, 0, radius, -theta, Math.PI + theta);

            this.displayObject.addChild(bgSprite);
            this.displayObject.addChild(outline);
        },

        initLiquid: function() {
            var padding = this.padding;
            var outlineFix = this.outlineFix;
            var radius = Math.floor(this.bulbDiameter / 2) - padding;
            var halfWidth = Math.floor(this.tubeWidth / 2) - padding;
            var width = halfWidth * 2;
            var height = Math.floor(this.tubeHeight) - padding;

            var bulbLiquid = new PIXI.Graphics();
            bulbLiquid.beginFill(this.liquidColor, 1);
            bulbLiquid.drawCircle(outlineFix, outlineFix, radius);
            bulbLiquid.endFill();

            var tubeLiquid = new PIXI.Graphics();
            var tubeLiquidMask = new PIXI.Graphics();

            var x = -halfWidth + outlineFix;
            var y = -radius - height + outlineFix - padding * 2;
            tubeLiquidMask.beginFill(0x000000, 1);
            tubeLiquidMask.drawRect(x, y, width, -y);
            tubeLiquidMask.drawCircle(outlineFix, y, halfWidth);
            tubeLiquidMask.endFill();
            tubeLiquid.mask = tubeLiquidMask;

            this.displayObject.addChild(bulbLiquid);
            this.displayObject.addChild(tubeLiquid);
            this.displayObject.addChild(tubeLiquidMask);

            this.tubeLiquid = tubeLiquid;

            this.drawTubeLiquid();
        },

        initTicks: function() {
            var ticks = new PIXI.Graphics();

            this.displayObject.addChild(ticks);
        },

        drawTubeLiquid: function() {
            var height = this.tubeHeight * this.value;
            if (height > this.tubeHeight * 2)
                height = this.tubeHeight * 2;
            if (height < 0)
                height = 0;

            var padding = this.padding;
            var outlineFix = this.outlineFix;
            var radius = Math.floor(this.bulbDiameter / 2);
            var halfWidth = Math.floor(this.tubeWidth / 2) - padding;
            var width = halfWidth * 2;

            var x = -halfWidth + outlineFix;
            var y = -radius - height + outlineFix + padding;

            this.tubeLiquid.clear();
            this.tubeLiquid.beginFill(this.liquidColor, 1);
            this.tubeLiquid.drawRect(x, y, width, -y);
            this.tubeLiquid.endFill();
        }

    });

    return ThermometerView;
});
<commit_msg>Accessor and modifiers<commit_after>define(function(require) {

    'use strict';

    var PIXI = require('pixi');
    var PixiView = require('../view');

    var Colors    = require('../../colors/colors');
    var Vector2   = require('../../math/vector2');
    var Rectangle = require('../../math/rectangle');

    var ThermometerView = PixiView.extend({

        initialize: function(options) {
            options = _.extend({
                bulbDiameter: 24,
                tubeWidth:    12,
                tubeHeight:   64,

                numberOfTicks: 7,

                lineColor: '#999',
                lineWidth: 1,
                lineAlpha: 1,

                fillColor: '#fff',
                fillAlpha: 0.3,

                liquidColor: '#ff3c00'
            }, options);

            this.bulbDiameter = options.bulbDiameter;
            this.tubeWidth = options.tubeWidth;
            this.tubeHeight = options.tubeHeight;

            this.lineColor = Colors.parseHex(options.lineColor);
            this.lineWidth = options.lineWidth;
            this.lineAlpha = options.lineAlpha;

            this.fillColor = Colors.parseHex(options.fillColor);
            this.fillAlpha = options.fillAlpha;

            this.liquidColor = Colors.parseHex(options.liquidColor);

            this.value = 0;

            this.padding = 2;
            this.outlineFix = 0.5;

            this.initGraphics();
        },

        initGraphics: function() {
            this.initShape();
            this.initLiquid();
            this.initTicks();
        },

        initShape: function() {
            var background = new PIXI.Graphics();
            var outline    = new PIXI.Graphics();

            // Only want to deal in whole pixels so it looks crisp
            var radius = Math.floor(this.bulbDiameter / 2);
            var halfWidth = Math.floor(this.tubeWidth / 2);
            var width = halfWidth * 2;
            var height = Math.floor(this.tubeHeight);

            // Filling it is easy.
            background.beginFill(this.fillColor, 1);
            background.drawCircle(0, 0, radius);
            background.drawRect(-halfWidth, 0, width, -radius - height);
            background.drawCircle(0, -radius - height, halfWidth);
            background.endFill();

            // (Except that applying an alpha to the graphics is actually buggy,
            //   so I'll create a texture and set an alpha on a sprite.)
            var bgSprite = new PIXI.Sprite(background.generateTexture());
            bgSprite.anchor.x = 0.5;
            bgSprite.anchor.y = (bgSprite.height - radius) / bgSprite.height;
            bgSprite.alpha = this.fillAlpha;

            // It's drawing the outline that's tricky.
            var theta = Math.acos(halfWidth / radius);
            var tubeBulbIntersectionY = -Math.sqrt(Math.pow(radius, 2) - Math.pow(halfWidth, 2));

            outline.lineStyle(this.lineWidth, this.lineColor, this.lineAlpha);
            outline.moveTo(-halfWidth, tubeBulbIntersectionY);
            outline.lineTo(-halfWidth, -radius - height);
            outline.arc(0, -radius - height, halfWidth, Math.PI, 0);
            outline.lineTo(halfWidth, tubeBulbIntersectionY);
            outline.arc(0, 0, radius, -theta, Math.PI + theta);

            this.displayObject.addChild(bgSprite);
            this.displayObject.addChild(outline);
        },

        initLiquid: function() {
            var padding = this.padding;
            var outlineFix = this.outlineFix;
            var radius = Math.floor(this.bulbDiameter / 2) - padding;
            var halfWidth = Math.floor(this.tubeWidth / 2) - padding;
            var width = halfWidth * 2;
            var height = Math.floor(this.tubeHeight) - padding;

            var bulbLiquid = new PIXI.Graphics();
            bulbLiquid.beginFill(this.liquidColor, 1);
            bulbLiquid.drawCircle(outlineFix, outlineFix, radius);
            bulbLiquid.endFill();

            var tubeLiquid = new PIXI.Graphics();
            var tubeLiquidMask = new PIXI.Graphics();

            var x = -halfWidth + outlineFix;
            var y = -radius - height + outlineFix - padding * 2;
            tubeLiquidMask.beginFill(0x000000, 1);
            tubeLiquidMask.drawRect(x, y, width, -y);
            tubeLiquidMask.drawCircle(outlineFix, y, halfWidth);
            tubeLiquidMask.endFill();
            tubeLiquid.mask = tubeLiquidMask;

            this.displayObject.addChild(bulbLiquid);
            this.displayObject.addChild(tubeLiquid);
            this.displayObject.addChild(tubeLiquidMask);

            this.tubeLiquid = tubeLiquid;

            this.drawTubeLiquid();
        },

        initTicks: function() {
            var ticks = new PIXI.Graphics();

            this.displayObject.addChild(ticks);
        },

        drawTubeLiquid: function() {
            var height = this.tubeHeight * this.value;
            if (height > this.tubeHeight * 2)
                height = this.tubeHeight * 2;
            if (height < 0)
                height = 0;

            var padding = this.padding;
            var outlineFix = this.outlineFix;
            var radius = Math.floor(this.bulbDiameter / 2);
            var halfWidth = Math.floor(this.tubeWidth / 2) - padding;
            var width = halfWidth * 2;

            var x = -halfWidth + outlineFix;
            var y = -radius - height + outlineFix + padding;

            this.tubeLiquid.clear();
            this.tubeLiquid.beginFill(this.liquidColor, 1);
            this.tubeLiquid.drawRect(x, y, width, -y);
            this.tubeLiquid.endFill();
        },

        setValue: function(value) {
            this.value = value;
            this.drawTubeLiquid();
        },

        getValue: function() {
            return this.value;
        },

        val: function(value) {
            if (value === undefined)
                return this.getValue();
            else
                this.setValue(value);
        }

    });

    return ThermometerView;
});
<|endoftext|>


<commit_before>import _ from 'lodash';
import { makeImmutable, unwrapImmutable } from '../immutable';

export default class StatePlugin {
  apply (component, source, root) {
    const components = component.components;

    const handlers = _.reduce(source.actions, function convertChildActions (obj, action, name) {
      if (_.isFunction(action)) obj[name] = action;
      if (components[name]) return createOverrideHandler(action);
      return obj;
    }, {});

    component.init = unwrapAfter(function init () {
      const state = _.mapValues(component.components, invokeInit);
      if (!handlers.initializeState) return state;
      return handlers.initializeState(makeImmutable(state));
    });

    component.update = unwrapAfter(function update (state, action) {
      if (!_.isArray(action.type)) return state;

      const [head, ...tail] = action.type;
      const isChildAction = _.has(components, head);
      const hasLocalHandler = _.isFunction(handlers[head]);

      if (isChildAction) {
        // No override; let the child component handle it
        if (!hasLocalHandler) {
          return components[head].update(makeImmutable(state[head]), _.defaults({ type: tail }, action));
        }

        // Action type is overriden, so use the override
        const child = components[head];
        const next = (childState, ...args) => child.update(makeImmutable(childState), ...args);
        return handlers[head](state, next, ...action.args);
      }

      if (hasLocalHandler) {
        return handlers[head](makeImmutable(state), ...action.args);
      }

      return state;
    });

    component.actions = _.mapValues(source.actions, (__, actionName) => {
      const type = component.path.concat(actionName);

      const fn = (...args) => root.dispatch({ type, args });
      // const fn = _.startsWith(actionName, '$')
      //   ? (...args) => root.dispatch({ type, args, component })
      //   : (...args) => root.dispatch({ type, args });

      Object.defineProperties(fn, {
        name: { value: actionName },
        displayName: { value: actionName }
      });

      return fn;
    });
  }
}

function createOverrideHandler (obj) {
  const strategy = _.mapValues(obj, (o) => (_.isFunction(o) ? o : createOverrideHandler(o)));

  return function reducer (state, next, action) {
    const override = _.get(strategy, action.type[0]);
    if (override) return override(state, next, action);
    return next(state, action);
  };
}

function invokeInit (it) {
  return it.init();
}

function unwrapAfter (fn) {
  return function unwrap (state, ...args) {
    return unwrapImmutable(fn(makeImmutable(state), ...args));
  };
}
<commit_msg>Fix child state bug<commit_after>import _ from 'lodash';
import { makeImmutable, unwrapImmutable } from '../immutable';

export default class StatePlugin {
  apply (component, source, root) {
    const components = component.components;

    const handlers = _.reduce(source.actions, function convertChildActions (obj, action, name) {
      if (_.isFunction(action)) obj[name] = action;
      if (components[name]) return createOverrideHandler(action);
      return obj;
    }, {});

    component.init = unwrapAfter(function init () {
      const state = _.mapValues(component.components, invokeInit);
      if (!handlers.initializeState) return state;
      return handlers.initializeState(makeImmutable(state));
    });

    component.update = unwrapAfter(function update (state, action) {
      if (!_.isArray(action.type)) return state;

      const [head, ...tail] = action.type;
      const isChildAction = _.has(components, head);
      const hasLocalHandler = _.isFunction(handlers[head]);

      if (isChildAction) {
        // No override; let the child component handle it
        if (!hasLocalHandler) {
          const childAction = _.defaults({ type: tail }, action);
          const nextChildState = components[head].update(state[head], childAction);
          return state.set(head, nextChildState);
        }

        // Action type is overriden, so use the override
        const child = components[head];
        const next = (childState, ...args) => child.update(makeImmutable(childState), ...args);
        return handlers[head](state, next, ...action.args);
      }

      if (hasLocalHandler) {
        return handlers[head](makeImmutable(state), ...action.args);
      }

      return state;
    });

    component.actions = _.mapValues(source.actions, (__, actionName) => {
      const type = component.path.concat(actionName);

      const fn = (...args) => root.dispatch({ type, args });
      // const fn = _.startsWith(actionName, '$')
      //   ? (...args) => root.dispatch({ type, args, component })
      //   : (...args) => root.dispatch({ type, args });

      Object.defineProperties(fn, {
        name: { value: actionName },
        displayName: { value: actionName }
      });

      return fn;
    });
  }
}

function createOverrideHandler (obj) {
  const strategy = _.mapValues(obj, (o) => (_.isFunction(o) ? o : createOverrideHandler(o)));

  return function reducer (state, next, action) {
    const override = _.get(strategy, action.type[0]);
    if (override) return override(state, next, action);
    return next(state, action);
  };
}

function invokeInit (it) {
  return it.init();
}

function unwrapAfter (fn) {
  return function unwrap (state, ...args) {
    return unwrapImmutable(fn(makeImmutable(state), ...args));
  };
}
<|endoftext|>


<commit_before>var test = require('tape');

test('index.deleteByQuery(query)', function(t) {
  deleteByQueryTest('callback', t);
  deleteByQueryTest('promise', t);
});

function deleteByQueryTest(asyncMode, mainTest) {
  mainTest.test('using a ' + asyncMode, function(t) {
    t.plan(12);

    var bind = require('lodash-compat/function/bind');
    var fauxJax = require('faux-jax');
    var parse = require('url-parse');
    var sinon = require('sinon');

    var createFixture = require('../../../utils/create-fixture');
    var fixture = createFixture({
      clientOptions: {
        timeout: 50
      }
    });
    var index = fixture.index;

    sinon.spy(index, 'search');
    sinon.spy(index, 'deleteObjects');
    sinon.spy(index, 'waitTask');
    sinon.spy(index, 'deleteByQuery');

    fauxJax.install();

    fauxJax.once('request', respondToSearch);

    if (asyncMode === 'callback') {
      index.deleteByQuery('salmon', function(err) {
        t.error(err, 'No error while deleting by query');
        t.equal(
          arguments.length,
          1,
          'Only one argument in callback'
        );
        fauxJax.restore();
      });
    } else if (asyncMode === 'promise') {
      var bind = require('lodash-compat/function/bind');
      index.deleteByQuery('salmon').then(function() {
        t.pass('No error while deleting by query');
        t.equal(
          arguments.length,
          1,
          'Only one argument in callback'
        );
        fauxJax.restore();
      }, bind(t.fail, t));
    }

    function respondToSearch(req) {
      // reset spy state, we do not care about first deleteByQuery call,
      // we know how it was called
      index.deleteByQuery.reset();

      // check how .search was called
      t.ok(index.search.calledOnce, 'search called');
      t.ok(
        index.search.calledWith('salmon', {
          attributesToRetrieve: 'objectID',
          hitsPerPage: 1000
        }),
        'search called with good params'
      );

      index.search.reset();

      fauxJax.once('request', respondToDeleteObjects);
      req.respond(
        200,
        {},
        JSON.stringify({
          nbHits: 800,
          hits: [{
            objectID: '1005',
          }, {
            objectID: '1006'
          }]
        })
      );
    }

    function respondToDeleteObjects(req) {
      t.ok(index.deleteObjects.calledOnce, 'deleteObjects called');
      t.ok(
        index.deleteObjects.calledWith([
          '1005',
          '1006'
        ]),
        'deleteObjects called with good params'
      );

      fauxJax.once('request', respondToWaitTask);
      req.respond(
        200,
        {},
        JSON.stringify({
          taskID: 2934
        })
      );
    }

    function respondToWaitTask(req) {
      t.ok(index.waitTask.calledOnce, 'index waitTask called');
      t.ok(
        index.waitTask.calledWith(2934),
        'index waitTask called with good params'
      );

      fauxJax.once('request', respondToSecondSearch);
      req.respond(
        200,
        {},
        JSON.stringify({
          status: 'published'
        })
      );
    }

    function respondToSecondSearch(req) {
      // deleteByQuery called again
      t.ok(index.deleteByQuery.calledOnce, 'index deleteByQuery called');
      t.ok(
        index.deleteByQuery.calledWith('salmon'),
        'index deleteByQuery called with good params'
      );

      // search called again
      t.ok(index.search.calledOnce, 'index search called');
      t.ok(
        index.search.calledWith('salmon', {
          attributesToRetrieve: 'objectID',
          hitsPerPage: 1000
        }),
        'index search called with good params'
      );

      // add check that deleteObjects was not called

      // this answers to deleteObject since search was served from cache
      // AH! we should clear the cache after each loop
      // but why does it works after, mystery
      req.respond(
        200,
        {},
        JSON.stringify({
          nbHits: 0
        })
      );

      index.search.restore();
      index.deleteObjects.restore();
      index.waitTask.restore();
      index.deleteByQuery.restore();
    }
  });
}
<commit_msg>remove uneeded timeout in deleteByQuery test<commit_after>var test = require('tape');

test('index.deleteByQuery(query)', function(t) {
  deleteByQueryTest('callback', t);
  deleteByQueryTest('promise', t);
});

function deleteByQueryTest(asyncMode, mainTest) {
  mainTest.test('using a ' + asyncMode, function(t) {
    t.plan(12);

    var bind = require('lodash-compat/function/bind');
    var fauxJax = require('faux-jax');
    var parse = require('url-parse');
    var sinon = require('sinon');

    var createFixture = require('../../../utils/create-fixture');
    var fixture = createFixture();
    var index = fixture.index;

    sinon.spy(index, 'search');
    sinon.spy(index, 'deleteObjects');
    sinon.spy(index, 'waitTask');
    sinon.spy(index, 'deleteByQuery');

    fauxJax.install();

    fauxJax.once('request', respondToSearch);

    if (asyncMode === 'callback') {
      index.deleteByQuery('salmon', function(err) {
        t.error(err, 'No error while deleting by query');
        t.equal(
          arguments.length,
          1,
          'Only one argument in callback'
        );
        fauxJax.restore();
      });
    } else if (asyncMode === 'promise') {
      var bind = require('lodash-compat/function/bind');
      index.deleteByQuery('salmon').then(function() {
        t.pass('No error while deleting by query');
        t.equal(
          arguments.length,
          1,
          'Only one argument in callback'
        );
        fauxJax.restore();
      }, bind(t.fail, t));
    }

    function respondToSearch(req) {
      // reset spy state, we do not care about first deleteByQuery call,
      // we know how it was called
      index.deleteByQuery.reset();

      // check how .search was called
      t.ok(index.search.calledOnce, 'search called');
      t.ok(
        index.search.calledWith('salmon', {
          attributesToRetrieve: 'objectID',
          hitsPerPage: 1000
        }),
        'search called with good params'
      );

      index.search.reset();

      fauxJax.once('request', respondToDeleteObjects);
      req.respond(
        200,
        {},
        JSON.stringify({
          nbHits: 800,
          hits: [{
            objectID: '1005',
          }, {
            objectID: '1006'
          }]
        })
      );
    }

    function respondToDeleteObjects(req) {
      t.ok(index.deleteObjects.calledOnce, 'deleteObjects called');
      t.ok(
        index.deleteObjects.calledWith([
          '1005',
          '1006'
        ]),
        'deleteObjects called with good params'
      );

      fauxJax.once('request', respondToWaitTask);
      req.respond(
        200,
        {},
        JSON.stringify({
          taskID: 2934
        })
      );
    }

    function respondToWaitTask(req) {
      t.ok(index.waitTask.calledOnce, 'index waitTask called');
      t.ok(
        index.waitTask.calledWith(2934),
        'index waitTask called with good params'
      );

      fauxJax.once('request', respondToSecondSearch);
      req.respond(
        200,
        {},
        JSON.stringify({
          status: 'published'
        })
      );
    }

    function respondToSecondSearch(req) {
      // deleteByQuery called again
      t.ok(index.deleteByQuery.calledOnce, 'index deleteByQuery called');
      t.ok(
        index.deleteByQuery.calledWith('salmon'),
        'index deleteByQuery called with good params'
      );

      // search called again
      t.ok(index.search.calledOnce, 'index search called');
      t.ok(
        index.search.calledWith('salmon', {
          attributesToRetrieve: 'objectID',
          hitsPerPage: 1000
        }),
        'index search called with good params'
      );

      // add check that deleteObjects was not called

      // this answers to deleteObject since search was served from cache
      // AH! we should clear the cache after each loop
      // but why does it works after, mystery
      req.respond(
        200,
        {},
        JSON.stringify({
          nbHits: 0
        })
      );

      index.search.restore();
      index.deleteObjects.restore();
      index.waitTask.restore();
      index.deleteByQuery.restore();
    }
  });
}
<|endoftext|>


<commit_before>function SharedBufferGeometry (material) {
  this.material = material;

  this.maxBufferSize = 1000000;
  this.geometries = [];
  this.current = null;
  this.strip = true;
  this.addBuffer(false);
}

SharedBufferGeometry.prototype = {
  restartPrimitive: function () {
    if (this.idx.position >= this.current.attributes.position.count) {
      this.addBuffer(false);
    } else if (this.idx.position !== 0) {
      var prev = (this.idx.position - 1) * 3;
      var position = this.current.attributes.position.array;
      this.addVertex(position[prev++], position[prev++], position[prev++]);

      this.idx.color++;
      this.idx.normal++;
      this.idx.uv++;
    }
  },

  remove: function (prevIdx, idx) {
    var pos = this.current.attributes.position.array;

    // Loop through all the attributes: position, color, uv, normal,...
    if (this.idx.position > idx.position) {
      for (key in this.idx) {
        var componentSize = key === 'uv' ? 2 : 3;
        var pos = (prevIdx[key]) * componentSize;
        var start = (idx[key] + 1) * componentSize;
        var end = this.idx[key] * componentSize;
        for (var i = start; i < end; i++) {
          this.current.attributes[key].array[pos++] = this.current.attributes[key].array[i];
        }
      }
    }

    for (key in this.idx) {
      var diff = (idx[key] - prevIdx[key]);
      this.idx[key] -= diff;
    }

    this.update();
  },

  undo: function (prevIdx) {
    for (let i = prevIdx.position; i < this.idx.position; i++) {
      this.current.attributes.position.setXYZ(i, 0, 0, 0);
      this.current.index.setXYZ(i, 0, 0, 0);
    }
    this.idx = prevIdx;
    this.update();
  },

  addBuffer: function (copyLast) {
    var geometry = new THREE.BufferGeometry();

    var vertices = new Float32Array(this.maxBufferSize * 3);
    var indices = new Uint32Array(this.maxBufferSize * 4.5);
    var normals = new Float32Array(this.maxBufferSize * 3);
    var uvs = new Float32Array(this.maxBufferSize * 2);
    var colors = new Float32Array(this.maxBufferSize * 3);

    var mesh = new THREE.Mesh(geometry, this.material);

    mesh.frustumCulled = false;
    mesh.vertices = vertices;

    this.object3D = new THREE.Object3D();
    var drawing = document.querySelector('.a-drawing');
    if (!drawing) {
      drawing = document.createElement('a-entity');
      drawing.className = "a-drawing";
      document.querySelector('a-scene').appendChild(drawing);
    }
    drawing.object3D.add(this.object3D);

    this.object3D.add(mesh);

    geometry.setDrawRange(0, 0);
    geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.setIndex(new THREE.BufferAttribute(indices, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2).setUsage(THREE.DynamicDrawUsage));
    geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage));

    this.previous = null;
    if (this.geometries.length > 0) {
      this.previous = this.current;
    }

    this.idx = {
      position: 0,
      uv: 0,
      normal: 0,
      color: 0
    };

    this.geometries.push(geometry);
    this.current = geometry;

    if (this.previous && copyLast) {
      var prev = (this.maxBufferSize - 2) * 3;
      var col = (this.maxBufferSize - 2) * 3;
      var uv = (this.maxBufferSize - 2) * 2;
      var norm = (this.maxBufferSize - 2) * 3;

      var position = this.previous.attributes.position.array;
      this.addVertex(position[prev++], position[prev++], position[prev++]);
      this.addVertex(position[prev++], position[prev++], position[prev++]);

      var normal = this.previous.attributes.normal.array;
      this.addNormal(normal[norm++], normal[norm++], normal[norm++]);
      this.addNormal(normal[norm++], normal[norm++], normal[norm++]);

      var color = this.previous.attributes.color.array;
      this.addColor(color[col++], color[col++], color[col++]);
      this.addColor(color[col++], color[col++], color[col++]);

      var uvs = this.previous.attributes.uv.array;

    }
  },

  addColor: function (r, g, b) {
    this.current.attributes.color.setXYZ(this.idx.color++, r, g, b);
  },

  addNormal: function (x, y, z) {
    this.current.attributes.normal.setXYZ(this.idx.normal++, x, y, z);
  },

  addVertex: function (x, y, z) {
    var buffer = this.current.attributes.position;
    if (this.idx.position === buffer.count) {
      this.addBuffer(true);
      buffer = this.current.attributes.position;
    }
    buffer.setXYZ(this.idx.position++, x, y, z);
    if (this.strip) {
      if ((this.idx.position + 1) % 2 == 0 && this.idx.position > 1) {
        /* Line brushes
          2---3
          | \ |
          0---1
          {0, 1, 2}, {2, 1, 3}
        */
        this.current.index.setXYZ(this.idx.position - 3, this.idx.position - 3, this.idx.position - 2, this.idx.position - 1);
        this.current.index.setXYZ(this.idx.position - 2, this.idx.position - 1, this.idx.position - 2, this.idx.position);
      }
    }
    else {
      if ((this.idx.position + 1) % 3 == 0) {
        /* Stamp brushes
          0---1  0
            \ |  | \
              2  3---2
          {0, 1, 2}, {2, 3, 0}
        */
        this.current.index.setXYZ(this.idx.position, this.idx.position - 2, this.idx.position - 1, this.idx.position);
      }
    }
  },

  addUV: function (u, v) {
    this.current.attributes.uv.setXY(this.idx.uv++, u, v);
  },

  update: function () {
    // Draw one less triangle to prevent indexing into blank positions
    // on an even-number-positioned undo
    this.current.setDrawRange(0, (this.idx.position * 3) - 4);

    this.current.attributes.color.needsUpdate = true;
    this.current.attributes.normal.needsUpdate = true;
    this.current.attributes.position.needsUpdate = true;
    this.current.attributes.uv.needsUpdate = true;
    this.current.index.needsUpdate = true;
  }
};

module.exports = SharedBufferGeometry;
<commit_msg>Adjust bufferattribute updateRanges<commit_after>function SharedBufferGeometry (material) {
  this.material = material;

  this.maxBufferSize = 1000000;
  this.geometries = [];
  this.current = null;
  this.strip = true;
  this.addBuffer(false);
}

SharedBufferGeometry.prototype = {
  restartPrimitive: function () {
    if (this.idx.position >= this.current.attributes.position.count) {
      this.addBuffer(false);
    } else if (this.idx.position !== 0) {
      var prev = (this.idx.position - 1) * 3;
      var position = this.current.attributes.position.array;
      this.addVertex(position[prev++], position[prev++], position[prev++]);

      this.idx.color++;
      this.idx.normal++;
      this.idx.uv++;
    }
  },

  remove: function (prevIdx, idx) {
    var pos = this.current.attributes.position.array;

    // Loop through all the attributes: position, color, uv, normal,...
    if (this.idx.position > idx.position) {
      for (key in this.idx) {
        var componentSize = key === 'uv' ? 2 : 3;
        var pos = (prevIdx[key]) * componentSize;
        var start = (idx[key] + 1) * componentSize;
        var end = this.idx[key] * componentSize;
        for (var i = start; i < end; i++) {
          this.current.attributes[key].array[pos++] = this.current.attributes[key].array[i];
        }
      }
    }

    for (key in this.idx) {
      var diff = (idx[key] - prevIdx[key]);
      this.idx[key] -= diff;
    }

    this.update();
  },

  undo: function (prevIdx) {
    for (let i = prevIdx.position; i < this.idx.position; i++) {
      this.current.attributes.position.setXYZ(i, 0, 0, 0);
      this.current.index.setXYZ(i, 0, 0, 0);
    }
    this.idx = prevIdx;
    this.update();
  },

  addBuffer: function (copyLast) {
    var geometry = new THREE.BufferGeometry();

    var vertices = new Float32Array(this.maxBufferSize * 3);
    var indices = new Uint32Array(this.maxBufferSize * 4.5);
    var normals = new Float32Array(this.maxBufferSize * 3);
    var uvs = new Float32Array(this.maxBufferSize * 2);
    var colors = new Float32Array(this.maxBufferSize * 3);

    var mesh = new THREE.Mesh(geometry, this.material);

    mesh.frustumCulled = false;
    mesh.vertices = vertices;

    this.object3D = new THREE.Object3D();
    var drawing = document.querySelector('.a-drawing');
    if (!drawing) {
      drawing = document.createElement('a-entity');
      drawing.className = "a-drawing";
      document.querySelector('a-scene').appendChild(drawing);
    }
    drawing.object3D.add(this.object3D);

    this.object3D.add(mesh);

    geometry.setDrawRange(0, 0);
    geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.attributes.position.updateRange.count = 0;
    geometry.setIndex(new THREE.BufferAttribute(indices, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.index.updateRange.count = 0;
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2).setUsage(THREE.DynamicDrawUsage));
    geometry.attributes.uv.updateRange.count = 0;
    geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.attributes.normal.updateRange.count = 0;
    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage));
    geometry.attributes.color.updateRange.count = 0;

    this.previous = null;
    if (this.geometries.length > 0) {
      this.previous = this.current;
    }

    this.idx = {
      position: 0,
      uv: 0,
      normal: 0,
      color: 0
    };

    this.geometries.push(geometry);
    this.current = geometry;

    if (this.previous && copyLast) {
      var prev = (this.maxBufferSize - 2) * 3;
      var col = (this.maxBufferSize - 2) * 3;
      var uv = (this.maxBufferSize - 2) * 2;
      var norm = (this.maxBufferSize - 2) * 3;

      var position = this.previous.attributes.position.array;
      this.addVertex(position[prev++], position[prev++], position[prev++]);
      this.addVertex(position[prev++], position[prev++], position[prev++]);

      var normal = this.previous.attributes.normal.array;
      this.addNormal(normal[norm++], normal[norm++], normal[norm++]);
      this.addNormal(normal[norm++], normal[norm++], normal[norm++]);

      var color = this.previous.attributes.color.array;
      this.addColor(color[col++], color[col++], color[col++]);
      this.addColor(color[col++], color[col++], color[col++]);

      var uvs = this.previous.attributes.uv.array;

    }
  },

  addColor: function (r, g, b) {
    this.current.attributes.color.setXYZ(this.idx.color++, r, g, b);
  },

  addNormal: function (x, y, z) {
    this.current.attributes.normal.setXYZ(this.idx.normal++, x, y, z);
  },

  addVertex: function (x, y, z) {
    var buffer = this.current.attributes.position;
    if (this.idx.position === buffer.count) {
      this.addBuffer(true);
      buffer = this.current.attributes.position;
    }
    buffer.setXYZ(this.idx.position++, x, y, z);
    if (this.strip) {
      if ((this.idx.position + 1) % 2 == 0 && this.idx.position > 1) {
        /* Line brushes
          2---3
          | \ |
          0---1
          {0, 1, 2}, {2, 1, 3}
        */
        this.current.index.setXYZ(this.idx.position - 3, this.idx.position - 3, this.idx.position - 2, this.idx.position - 1);
        this.current.index.setXYZ(this.idx.position - 2, this.idx.position - 1, this.idx.position - 2, this.idx.position);
      }
    }
    else {
      if ((this.idx.position + 1) % 3 == 0) {
        /* Stamp brushes
          0---1  0
            \ |  | \
              2  3---2
          {0, 1, 2}, {2, 3, 0}
        */
        this.current.index.setXYZ(this.idx.position, this.idx.position - 2, this.idx.position - 1, this.idx.position);
      }
    }
  },

  addUV: function (u, v) {
    this.current.attributes.uv.setXY(this.idx.uv++, u, v);
  },

  update: function () {
    // Draw one less triangle to prevent indexing into blank positions
    // on an even-number-positioned undo
    this.current.setDrawRange(0, (this.idx.position * 3) - 4);

    this.current.attributes.color.updateRange.count = this.idx.position * 3;
    this.current.attributes.color.needsUpdate = true;
    this.current.attributes.normal.updateRange.count = this.idx.position * 3;
    this.current.attributes.normal.needsUpdate = true;
    this.current.attributes.position.updateRange.count = this.idx.position * 3;
    this.current.attributes.position.needsUpdate = true;
    this.current.attributes.uv.updateRange.count = this.idx.position * 2;
    this.current.attributes.uv.needsUpdate = true;
    this.current.index.updateRange.count = this.idx.position * 3;
    this.current.index.needsUpdate = true;
  }
};

module.exports = SharedBufferGeometry;
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import Plotly from 'plotly.js';

const VALID_EVENT_KEYS = ['x', 'y', 'z', 'text', 'labels', 'values', 'pointNumber',
                          'curveNumber'];

const filterEventData = (eventData) => {
    // Create a new object
    return {
        // `Array.prototype.map` creates a new array of points.
        points: eventData.points.map((pointData) =>
            VALID_EVENT_KEYS.reduce((result, currentKey) => {
                const currentVal = pointData[currentKey];
                return Object.assign(result, {[currentKey]: currentVal});
            }, {})
        )
    };
};

export default class PlotlyGraph extends Component {
    constructor(props) {
        super(props);
        this.bindEvents = this.bindEvents.bind(this);
    }

    plot(props) {
        const {id, figure} = props;
        return Plotly.newPlot(id, figure);
    }

    bindEvents(props) {
        const {id, fireEvent, valueChanged} = props;

        // Get DOM node to call jQuery-provided `on` event binder.
        const gd = document.getElementById(id);

        gd.on('plotly_click', (eventData) => {
            const clickData = filterEventData(eventData);
            if (valueChanged) {
                valueChanged({clickData});
            }
            if (fireEvent) fireEvent({event: 'click'});
        });
        gd.on('plotly_hover', (eventData) => {
            const hoverData = filterEventData(eventData);
            if (valueChanged) valueChanged({hoverData});
            if (fireEvent) fireEvent({event: 'hover'})
        });
        gd.on('plotly_selected', (eventData) => {
            const selectedData = filterEventData(eventData);
            if (valueChanged) valueChanged({selectedData});
            if (fireEvent) fireEvent({event: 'selected'});
        });
        // TODO - Is there a zoom event?
    }

    componentDidMount() {
        this.plot(this.props).then(() => {
            this.bindEvents(this.props);
            window.addEventListener('resize', () => {
                Plotly.Plots.resize(document.getElementById(this.props.id));
            });
        });
    }

    componentWillUnmount() {
        if (this.eventEmitter) {
            this.eventEmitter.removeAllListeners();
        }
    }

    shouldComponentUpdate() {
        // Never re-render after initialization, let Plotly.js own the DOM node.
        return false;
    }

    componentWillReceiveProps(nextProps) {
        // TODO optimize this check
        const figureChanged = JSON.stringify(this.props.figure) !== JSON.stringify(nextProps.figure)

        /*
         * Rebind events in case fireEvent or valueChanged
         * wasn't defined on initial render
         * TODO - Is it safe to rebind events?
         */
        const shouldBindEvents = (
            (!this.props.valueChanged && nextProps.valueChanged) ||
            (!this.props.fireEvent && nextProps.fireEvent)
        );
        if (figureChanged) {
            this.plot(nextProps).then(() => {
                if(shouldBindEvents) this.bindEvents(nextProps);
            });
        } else if (shouldBindEvents) {
            this.bindEvents(nextProps);
        }
    }

    render(){
        const {style, id} = this.props

        return (
            <div
                id={id}
                style={style}
            />
        );
    }
}

PlotlyGraph.propTypes = {
    /**
     * Data from latest click event
     */
    clickData: PropTypes.object,

    /**
     * Data from latest hover event
     */
    hoverData: PropTypes.object,

    /**
     * Data from latest select event
     */
    selectedData: PropTypes.object,

    /**
     * Data from latest zoom event
     */
    zoomData: PropTypes.object,

    /**
     * Plotly `figure` object. See schema:
     * https://plot.ly/javascript/reference
     */
    figure: PropTypes.object,

    /**
     * Generic style overrides on the plot div
     */
    style: PropTypes.object,

    /**
     * Function that updates the state tree.
     */
    valueChanged: PropTypes.func,

    /**
     * Function that fires events
     */
    fireEvent: PropTypes.func
}

PlotlyGraph.defaultProps = {
    clickData: null,
    hoverData: null,
    selectedData: null,
    zoomData: null,
    figure: {data: [], layout: {}},
    layout: {}
};
<commit_msg>rebind events after every newPlot<commit_after>import React, {Component, PropTypes} from 'react';
import Plotly from 'plotly.js';

const VALID_EVENT_KEYS = ['x', 'y', 'z', 'text', 'labels', 'values', 'pointNumber',
                          'curveNumber'];

const filterEventData = (eventData) => {
    // Create a new object
    return {
        // `Array.prototype.map` creates a new array of points.
        points: eventData.points.map((pointData) =>
            VALID_EVENT_KEYS.reduce((result, currentKey) => {
                const currentVal = pointData[currentKey];
                return Object.assign(result, {[currentKey]: currentVal});
            }, {})
        )
    };
};

export default class PlotlyGraph extends Component {
    constructor(props) {
        super(props);
        this.bindEvents = this.bindEvents.bind(this);
    }

    plot(props) {
        const {id, figure} = props;
        return Plotly.newPlot(id, figure).then(() => {
            this.bindEvents(props);
        });
    }

    bindEvents(props) {
        const {id, fireEvent, valueChanged} = props;

        // Get DOM node to call jQuery-provided `on` event binder.
        const gd = document.getElementById(id);

        gd.on('plotly_click', (eventData) => {
            const clickData = filterEventData(eventData);
            if (valueChanged) {
                valueChanged({clickData});
            }
            if (fireEvent) fireEvent({event: 'click'});
        });
        gd.on('plotly_hover', (eventData) => {
            const hoverData = filterEventData(eventData);
            if (valueChanged) valueChanged({hoverData});
            if (fireEvent) fireEvent({event: 'hover'})
        });
        gd.on('plotly_selected', (eventData) => {
            const selectedData = filterEventData(eventData);
            if (valueChanged) valueChanged({selectedData});
            if (fireEvent) fireEvent({event: 'selected'});
        });
        // TODO - Is there a zoom event?
    }

    componentDidMount() {
        this.plot(this.props).then(() => {
            window.addEventListener('resize', () => {
                Plotly.Plots.resize(document.getElementById(this.props.id));
            });
        });
    }

    componentWillUnmount() {
        if (this.eventEmitter) {
            this.eventEmitter.removeAllListeners();
        }
    }

    shouldComponentUpdate() {
        // Never re-render after initialization, let Plotly.js own the DOM node.
        return false;
    }

    componentWillReceiveProps(nextProps) {
        // TODO optimize this check
        const figureChanged = JSON.stringify(this.props.figure) !== JSON.stringify(nextProps.figure)

        /*
         * Rebind events in case fireEvent or valueChanged
         * wasn't defined on initial render
         * TODO - Is it safe to rebind events?
         */
        const shouldBindEvents = (
            (!this.props.valueChanged && nextProps.valueChanged) ||
            (!this.props.fireEvent && nextProps.fireEvent)
        );
        if (figureChanged) {
            this.plot(nextProps);
        } else if (shouldBindEvents) {
            this.bindEvents(nextProps);
        }
    }

    render(){
        const {style, id} = this.props

        return (
            <div
                id={id}
                style={style}
            />
        );
    }
}

PlotlyGraph.propTypes = {
    /**
     * Data from latest click event
     */
    clickData: PropTypes.object,

    /**
     * Data from latest hover event
     */
    hoverData: PropTypes.object,

    /**
     * Data from latest select event
     */
    selectedData: PropTypes.object,

    /**
     * Data from latest zoom event
     */
    zoomData: PropTypes.object,

    /**
     * Plotly `figure` object. See schema:
     * https://plot.ly/javascript/reference
     */
    figure: PropTypes.object,

    /**
     * Generic style overrides on the plot div
     */
    style: PropTypes.object,

    /**
     * Function that updates the state tree.
     */
    valueChanged: PropTypes.func,

    /**
     * Function that fires events
     */
    fireEvent: PropTypes.func
}

PlotlyGraph.defaultProps = {
    clickData: null,
    hoverData: null,
    selectedData: null,
    zoomData: null,
    figure: {data: [], layout: {}},
    layout: {}
};
<|endoftext|>


<commit_before>exports.calculate = function (avatar) {
  const repositories = avatar.repository
  const streak = avatar.currentStreak
  const information = avatar.information
  const hp = calculateHP()
  const mp = calculateMP()
  const pAtack = calculatePAtack()
  const pDef = calculatePDef()
  const castSpeed = calculateCastSpeed()
  const criticalChance = calculateCriticalChance()
  const accuracy = calculateAccuracy()
  const stamina = calculateStamina()

  function calculateHP () {
    return ((information.commits + streak) * information.commitsAverage) + (information.followers + getFollowingInformation() + getStarInformation())
  }

  function calculateMP () {
    return ((information.commits + streak) * information.commitsAverage)
  }

  function calculatePAtack () {
    return ((getRepositoryForks() + getRepositoryStar() + repositories.repositories.full.length + information.commits) * getRepositoriesRelevants()) + (information.commits * 2)
  }

  function calculatePDef () {
    return ((getRepositoryForks() + information.followers + getFollowingInformation() + getRepositoryStar() + repositories.repositories.full.length) * getRepositoriesRelevants()) +
      (information.commits * information.commitsAverage)
  }

  function calculateCastSpeed () {
    return (information.commits + streak) * getInformationOrganization()
  }

  function calculateCriticalChance () {
    return (information.commits + streak + information.followers + getFollowingInformation()) * getInformationOrganization()
  }

  function calculateAccuracy () {
    return ((information.followers) + getFollowingInformation()) * ((getInformationOrganization() * information.commitsAverage) * getRepositoriesRelevants()) + information.commits + streak
  }

  function calculateStamina () {
    return (information.commits * information.commitsAverage)
  }

  function getInformationOrganization () {
    return information.organizations === 0 ? 1 : information.organizations >= 5 ? 5 : information.organizations
  }

  function getStarInformation () {
    return information.stars > 1000 ? 1000 : information.stars
  }

  function getFollowingInformation () {
    return information.following > 1000 ? 1000 : information.following
  }

  function getRepositoryStar () {
    return repositories.stars >= 4 ? (repositories.stars / 2) : 1
  }

  function getRepositoryForks () {
    return repositories.forks >= 4 ? (repositories.forks / 2) : 1
  }

  function getRepositoriesRelevants () {
    return repositories.repositories.relevants.length === 0 ? 1 : repositories.repositories.relevants.length
  }

  return {
    HP: parseInt(hp) <= 0 ? 1 : parseInt(hp),
    TOTAL_HP: parseInt(hp) <= 0 ? 1 : parseInt(hp),
    MP: parseInt(mp) <= 0 ? 1 : parseInt(mp),
    P_ATCK: parseInt(pAtack) <= 0 ? 1 : parseInt(pAtack),
    P_DEF: parseInt(pDef) <= 0 ? 1 : parseInt(pDef),
    SPEED: parseInt(castSpeed) <= 0 ? 1 : parseInt(castSpeed),
    CRITICAL: parseInt(criticalChance) <= 0 ? 1 : parseInt(criticalChance),
    ACCURACY: parseInt(accuracy),
    STAMINA: parseInt(stamina)
  }
}
<commit_msg>add accuracy validator<commit_after>exports.calculate = function (avatar) {
  const repositories = avatar.repository
  const streak = avatar.currentStreak
  const information = avatar.information
  const hp = calculateHP()
  const mp = calculateMP()
  const pAtack = calculatePAtack()
  const pDef = calculatePDef()
  const castSpeed = calculateCastSpeed()
  const criticalChance = calculateCriticalChance()
  const accuracy = calculateAccuracy()
  const stamina = calculateStamina()

  function calculateHP () {
    return ((information.commits + streak) * information.commitsAverage) + (information.followers + getFollowingInformation() + getStarInformation())
  }

  function calculateMP () {
    return ((information.commits + streak) * information.commitsAverage)
  }

  function calculatePAtack () {
    return ((getRepositoryForks() + getRepositoryStar() + repositories.repositories.full.length + information.commits) * getRepositoriesRelevants()) + (information.commits * 2)
  }

  function calculatePDef () {
    return ((getRepositoryForks() + information.followers + getFollowingInformation() + getRepositoryStar() + repositories.repositories.full.length) * getRepositoriesRelevants()) +
      (information.commits * information.commitsAverage)
  }

  function calculateCastSpeed () {
    return (information.commits + streak) * getInformationOrganization()
  }

  function calculateCriticalChance () {
    return (information.commits + streak + information.followers + getFollowingInformation()) * getInformationOrganization()
  }

  function calculateAccuracy () {
    return ((information.followers) + getFollowingInformation()) * ((getInformationOrganization() * information.commitsAverage) * getRepositoriesRelevants()) + information.commits + streak
  }

  function calculateStamina () {
    return (information.commits * information.commitsAverage)
  }

  function getInformationOrganization () {
    return information.organizations === 0 ? 1 : information.organizations >= 5 ? 5 : information.organizations
  }

  function getStarInformation () {
    return information.stars > 1000 ? 1000 : information.stars
  }

  function getFollowingInformation () {
    return information.following > 1000 ? 1000 : information.following
  }

  function getRepositoryStar () {
    return repositories.stars >= 4 ? (repositories.stars / 2) : 1
  }

  function getRepositoryForks () {
    return repositories.forks >= 4 ? (repositories.forks / 2) : 1
  }

  function getRepositoriesRelevants () {
    return repositories.repositories.relevants.length === 0 ? 1 : repositories.repositories.relevants.length
  }

  return {
    HP: parseInt(hp) <= 0 ? 1 : parseInt(hp),
    TOTAL_HP: parseInt(hp) <= 0 ? 1 : parseInt(hp),
    MP: parseInt(mp) <= 0 ? 1 : parseInt(mp),
    P_ATCK: parseInt(pAtack) <= 0 ? 1 : parseInt(pAtack),
    P_DEF: parseInt(pDef) <= 0 ? 1 : parseInt(pDef),
    SPEED: parseInt(castSpeed) <= 0 ? 1 : parseInt(castSpeed),
    CRITICAL: parseInt(criticalChance) <= 0 ? 1 : parseInt(criticalChance),
    ACCURACY: parseInt(accuracy) <= 0 ? 1 : parseInt(accuracy),
    STAMINA: parseInt(stamina)
  }
}
<|endoftext|>


<commit_before>/**
 * The typer gremlin types keys on the keyboard
 *
 * Note that keyboard events must be localized somewhere on screen, so this
 * gremlins picks a random screen location first.
 *
 * By default, the typer gremlin activity is showed by a letter surrounded by
 * a red circle.
 *
 *   var typerGremlin = gremlins.species.typer();
 *   horde.gremlin(typerGremlin);
 *
 * The typerGremlin gremlin can be customized as follows:
 *
 *   typerGremlin.eventTypes(['keypress', 'keyup', 'keydown']); // types of events to trigger
 *   typerGremlin.showAction(function(element) { // show the gremlin activity on screen });
 *   typerGremlin.logger(loggerObject); // inject a logger
 *   typerGremlin.randomizer(randomizerObject); // inject a randomizer
 *
 */
define(function(require) {
    "use strict";

    var configurable = require('../utils/configurable');
    var Chance = require('../vendor/chance');
    var RandomizerRequiredException = require('../exceptions/randomizerRequired');

    return function() {

        var document = window.document,
            documentElement = document.documentElement,
            body = document.body;

        var defaultEventTypes = ['keypress', 'keyup', 'keydown'];

        function defaultKeyGenerator() {
            return config.randomizer.natural({ min: 3, max: 254});
        }

        function defaultTargetElement(x, y) {
            return document.elementFromPoint(x, y);
        }

        function defaultShowAction(targetElement, x, y, key) {
            var typeSignal = document.createElement('div');
            typeSignal.style.zIndex = 2000;
            typeSignal.style.border = "3px solid orange";
            typeSignal.style['border-radius'] = '50%'; // Chrome
            typeSignal.style.borderRadius = '50%';     // Mozilla
            typeSignal.style.width = "40px";
            typeSignal.style.height = "40px";
            typeSignal.style['box-sizing'] = 'border-box';
            typeSignal.style.position = "absolute";
            typeSignal.style.webkitTransition = 'opacity 1s ease-out';
            typeSignal.style.mozTransition = 'opacity 1s ease-out';
            typeSignal.style.transition = 'opacity 1s ease-out';
            typeSignal.style.left = x + 'px';
            typeSignal.style.top = y + 'px';
            typeSignal.style.textAlign = 'center';
            typeSignal.style.paddingTop = '7px';
            typeSignal.innerHTML = String.fromCharCode(key);

            var element = body.appendChild(typeSignal);
            setTimeout(function () {
                body.removeChild(element);
            }, 1000);
            setTimeout(function () {
                element.style.opacity = 0;
            }, 50);
        }

        /**
         * @mixin
         */
        var config = {
            eventTypes: defaultEventTypes,
            showAction: defaultShowAction,
            keyGenerator: defaultKeyGenerator,
            targetElement: defaultTargetElement,
            logger:     null,
            randomizer: null
        };

        /**
         * @mixes config
         */
        function typerGremlin() {
            if (!config.randomizer) {
                throw new RandomizerRequiredException();
            }

            var keyboardEvent = document.createEventObject ? document.createEventObject() : document.createEvent("Events"),
                eventType = config.randomizer.pick(config.eventTypes),
                key = config.keyGenerator(),
                posX = config.randomizer.natural({ max: documentElement.clientWidth - 1 }),
                posY = config.randomizer.natural({ max: documentElement.clientHeight - 1 }),
                targetElement = config.targetElement(posX, posY);

            if(keyboardEvent.initEvent){
                keyboardEvent.initEvent(eventType, true, true);
            }

            keyboardEvent.keyCode = key;
            keyboardEvent.which = key;
            keyboardEvent.keyCodeVal = key;

            targetElement.dispatchEvent ? targetElement.dispatchEvent(keyboardEvent) : targetElement.fireEvent("on" + eventType, keyboardEvent);

            if (typeof config.showAction === 'function') {
                config.showAction(targetElement, posX, posY, key);
            }

            if (config.logger && typeof config.logger.log == 'function') {
                config.logger.log('gremlin', 'typer       type', String.fromCharCode(key), 'at', posX, posY);
            }
        }

        configurable(typerGremlin, config);

        return typerGremlin;
    };
});
<commit_msg>Update typer.js<commit_after>/**
 * The typer gremlin types keys on the keyboard
 *
 * Note that keyboard events must be localized somewhere on screen, so this
 * gremlins picks a random screen location first.
 *
 * By default, the typer gremlin activity is showed by a letter surrounded by
 * a orange circle with a keyname on it.
 *
 *   var typerGremlin = gremlins.species.typer();
 *   horde.gremlin(typerGremlin);
 *
 * The typerGremlin gremlin can be customized as follows:
 *
 *   typerGremlin.eventTypes(['keypress', 'keyup', 'keydown']); // types of events to trigger
 *   typerGremlin.showAction(function(element) { // show the gremlin activity on screen });
 *   typerGremlin.logger(loggerObject); // inject a logger
 *   typerGremlin.randomizer(randomizerObject); // inject a randomizer
 *
 */
define(function(require) {
    "use strict";

    var configurable = require('../utils/configurable');
    var Chance = require('../vendor/chance');
    var RandomizerRequiredException = require('../exceptions/randomizerRequired');

    return function() {

        var document = window.document,
            documentElement = document.documentElement,
            body = document.body;

        var defaultEventTypes = ['keypress', 'keyup', 'keydown'];

        function defaultKeyGenerator() {
            return config.randomizer.natural({ min: 3, max: 254});
        }

        function defaultTargetElement(x, y) {
            return document.elementFromPoint(x, y);
        }

        function defaultShowAction(targetElement, x, y, key) {
            var typeSignal = document.createElement('div');
            typeSignal.style.zIndex = 2000;
            typeSignal.style.border = "3px solid orange";
            typeSignal.style['border-radius'] = '50%'; // Chrome
            typeSignal.style.borderRadius = '50%';     // Mozilla
            typeSignal.style.width = "40px";
            typeSignal.style.height = "40px";
            typeSignal.style['box-sizing'] = 'border-box';
            typeSignal.style.position = "absolute";
            typeSignal.style.webkitTransition = 'opacity 1s ease-out';
            typeSignal.style.mozTransition = 'opacity 1s ease-out';
            typeSignal.style.transition = 'opacity 1s ease-out';
            typeSignal.style.left = x + 'px';
            typeSignal.style.top = y + 'px';
            typeSignal.style.textAlign = 'center';
            typeSignal.style.paddingTop = '7px';
            typeSignal.innerHTML = String.fromCharCode(key);

            var element = body.appendChild(typeSignal);
            setTimeout(function () {
                body.removeChild(element);
            }, 1000);
            setTimeout(function () {
                element.style.opacity = 0;
            }, 50);
        }

        /**
         * @mixin
         */
        var config = {
            eventTypes: defaultEventTypes,
            showAction: defaultShowAction,
            keyGenerator: defaultKeyGenerator,
            targetElement: defaultTargetElement,
            logger:     null,
            randomizer: null
        };

        /**
         * @mixes config
         */
        function typerGremlin() {
            if (!config.randomizer) {
                throw new RandomizerRequiredException();
            }

            var keyboardEvent = document.createEventObject ? document.createEventObject() : document.createEvent("Events"),
                eventType = config.randomizer.pick(config.eventTypes),
                key = config.keyGenerator(),
                posX = config.randomizer.natural({ max: documentElement.clientWidth - 1 }),
                posY = config.randomizer.natural({ max: documentElement.clientHeight - 1 }),
                targetElement = config.targetElement(posX, posY);

            if(keyboardEvent.initEvent){
                keyboardEvent.initEvent(eventType, true, true);
            }

            keyboardEvent.keyCode = key;
            keyboardEvent.which = key;
            keyboardEvent.keyCodeVal = key;

            targetElement.dispatchEvent ? targetElement.dispatchEvent(keyboardEvent) : targetElement.fireEvent("on" + eventType, keyboardEvent);

            if (typeof config.showAction === 'function') {
                config.showAction(targetElement, posX, posY, key);
            }

            if (config.logger && typeof config.logger.log == 'function') {
                config.logger.log('gremlin', 'typer       type', String.fromCharCode(key), 'at', posX, posY);
            }
        }

        configurable(typerGremlin, config);

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


<commit_before>import { Utils } from 'meteor/vulcan:core';
import cheerio from 'cheerio';
import { Comments } from '../comments'
import { Posts } from './collection';
import { questionAnswersSort } from '../comments/views';
import { getSetting } from 'meteor/vulcan:core';

// Number of headings below which a table of contents won't be generated.
const MIN_HEADINGS_FOR_TOC = 3;

// Tags which define headings. Currently
-
, , and . Excludes
//
 and
 because their usage in historical (HTML) wasn't as a ToC-
// worthy heading.
const headingTags = {
  h1: 1,
  h2: 2,
  h3: 3,
  h4: 4,
  //  and  are at the same level
  strong: 7,
  b: 7,
}

const headingIfWholeParagraph = {
  strong: true,
  b: true,
};

const headingSelector = _.keys(headingTags).join(",");

// Given an HTML document, extract a list of sections for a table of contents
// from it, and add anchors. The result is modified HTML with added anchors,
// plus a JSON array of sections, where each section has a
// `title`, `anchor`, and `level`, like this:
//   {
//     html: ",
//     sections: [
//       {title: "Preamble", anchor: "preamble", level: 1},
//       {title: "My Cool Idea", anchor: "mycoolidea", level: 1},
//         {title: "An Aspect of My Cool Idea", anchor:"anaspectofmycoolidea", level: 2},
//         {title: "Why This Is Neat", anchor:"whythisisneat", level: 2},
//       {title: "Conclusion", anchor: "conclusion", level: 1},
//     ]
//   }
export function extractTableOfContents(postHTML)
{
  if (!postHTML) return null;
  const postBody = cheerio.load(postHTML);
  let headings = [];
  let usedAnchors = {};

  // First, find the headings in the document, create a linear list of them,
  // and insert anchors at each one.
  let headingTags = postBody(headingSelector);
  for (let i=0; i<headingTags.length; i++) {
    let tag = headingTags[i];

    if (tagIsHeadingIfWholeParagraph(tag.tagName) && !tagIsWholeParagraph(tag)) {
      continue;
    }

    let title = cheerio(tag).text();

    if (title && title.trim()!=="") {
      let anchor = titleToAnchor(title, usedAnchors);
      usedAnchors[anchor] = true;
      cheerio(tag).attr("id", anchor);
      headings.push({
        title: title,
        anchor: anchor,
        level: tagToHeadingLevel(tag.tagName),
      });
    }
  }

  // Filter out unused heading levels, mapping the heading levels to consecutive
  // numbers starting from 1. So if a post uses
,
 and , those
  // will be levels 1, 2, and 3 (not 1, 3 and 7).

  // Get a list of heading levels used
  let headingLevelsUsedDict = {};
  for(let i=0; i<headings.length; i++)
    headingLevelsUsedDict[headings[i].level] = true;

  // Generate a mapping from raw heading levels to compressed heading levels
  let headingLevelsUsed = _.keys(headingLevelsUsedDict).sort();
  let headingLevelMap = {};
  for(let i=0; i<headingLevelsUsed.length; i++)
    headingLevelMap[ headingLevelsUsed[i] ] = i;

  // Mark sections with compressed heading levels
  for(let i=0; i<headings.length; i++)
    headings[i].level = headingLevelMap[headings[i].level]+1;

  if (headings.length) {
    headings.push({divider:true, level: 0, anchor: "postHeadingsDivider"})
  }
  return headings
}

const reservedAnchorNames = ["top", "comments"];

// Given the text in a heading block and a dict of anchors that have been used
// in the post so far, generate an anchor, and return it. An anchor is a
// URL-safe string that can be used for within-document links, and which is
// not one of a few reserved anchor names.
function titleToAnchor(title, usedAnchors)
{
  let charsToUse = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
  let sb = [];

  for(let i=0; i<title.length; i++) {
    let ch = title.charAt(i);
    if(charsToUse.indexOf(ch) >= 0) {
      sb.push(ch);
    } else {
      sb.push('_');
    }
  }

  let anchor = sb.join('');
  if(!usedAnchors[anchor] && !_.find(reservedAnchorNames, x=>x===anchor))
    return anchor;

  let anchorSuffix = 1;
  while(usedAnchors[anchor + anchorSuffix])
    anchorSuffix++;
  return anchor+anchorSuffix;
}

// `<b>` and `<strong>` tags are headings iff they are the only thing in their
// paragraph. Return whether the given tag name is a tag with that property
// (ie, is `<strong>` or `<b>`).
function tagIsHeadingIfWholeParagraph(tagName)
{
  return tagName.toLowerCase() in headingIfWholeParagraph;
}

function tagIsWholeParagraph(tag) {
  if (!tag) return false;
  let parents = cheerio(tag).parent();
  if (!parents || !parents.length) return false;
  let parent = parents[0];
  if (parent.tagName.toLowerCase() !== 'p') return false;
  let selfAndSiblings = cheerio(parent).contents();
  if (selfAndSiblings.length != 1) return false;

  return true;
}

function tagToHeadingLevel(tagName)
{
  let lowerCaseTagName = tagName.toLowerCase();
  if (lowerCaseTagName in headingTags)
    return headingTags[lowerCaseTagName];
  else if (lowerCaseTagName in headingIfWholeParagraph)
    return headingIfWholeParagraph[lowerCaseTagName];
  else
    return 0;
}

async function getTocAnswers (document) {
  if (!document.question) return []

  let answersTerms = {
    answer:true,
    postId: document._id,
    deleted:false,
  }
  if (getSetting('forumType') === 'AlignmentForum') {
    answersTerms.af = true
  }
  const answers = await Comments.find(answersTerms, {sort:questionAnswersSort}).fetch()
  const answerSections = answers.map((answer) => ({
    title: `${answer.baseScore} ${answer.author}`,
    answer: answer,
    anchor: answer._id,
    level: 2
  }))

  if (answerSections.length) {
    return [{anchor: "answers", level:1, title:"Answers"}, ...answerSections]
  } else {
    return []
  }
}

async function getTocComments (document) {
  const commentSelector = {
    answer: false,
    parentAnswerId: null,
    postId: document._id

  }
  if (document.af && getSetting('forumType') === 'AlignmentForum') {
    commentSelector.af = true
  }
  const commentCount = await Comments.find(commentSelector).count()
  return [{anchor:"comments", level:0, title: Posts.getCommentCountStr(document, commentCount)}]
}

const getTableOfContentsData = async (document, args, options) => {
  const { html } = document.contents || {}
  let tocSections = extractTableOfContents(html) || []

  const tocAnswers = await getTocAnswers(document)
  const tocComments = await getTocComments(document)

  tocSections.push(...tocAnswers)
  tocSections.push(...tocComments)

  if (tocSections.length >= MIN_HEADINGS_FOR_TOC) {
    return {
      html: html,
      sections: tocSections,
      headingsCount: tocSections.length
    }
  }
}

Utils.getTableOfContentsData = getTableOfContentsData;
Utils.extractTableOfContents = extractTableOfContents;
<commit_msg>fix tocIssues<commit_after>import { Utils } from 'meteor/vulcan:core';
import cheerio from 'cheerio';
import { Comments } from '../comments'
import { Posts } from './collection';
import { questionAnswersSort } from '../comments/views';
import { getSetting } from 'meteor/vulcan:core';

// Number of headings below which a table of contents won't be generated.
const MIN_HEADINGS_FOR_TOC = 3;

// Tags which define headings. Currently
-
, , and . Excludes
//
 and
 because their usage in historical (HTML) wasn't as a ToC-
// worthy heading.
const headingTags = {
  h1: 1,
  h2: 2,
  h3: 3,
  h4: 4,
  //  and  are at the same level
  strong: 7,
  b: 7,
}

const headingIfWholeParagraph = {
  strong: true,
  b: true,
};

const headingSelector = _.keys(headingTags).join(",");

// Given an HTML document, extract a list of sections for a table of contents
// from it, and add anchors. The result is modified HTML with added anchors,
// plus a JSON array of sections, where each section has a
// `title`, `anchor`, and `level`, like this:
//   {
//     html: ",
//     sections: [
//       {title: "Preamble", anchor: "preamble", level: 1},
//       {title: "My Cool Idea", anchor: "mycoolidea", level: 1},
//         {title: "An Aspect of My Cool Idea", anchor:"anaspectofmycoolidea", level: 2},
//         {title: "Why This Is Neat", anchor:"whythisisneat", level: 2},
//       {title: "Conclusion", anchor: "conclusion", level: 1},
//     ]
//   }
export function extractTableOfContents(postHTML)
{
  if (!postHTML) return null;
  const postBody = cheerio.load(postHTML);
  let headings = [];
  let usedAnchors = {};

  // First, find the headings in the document, create a linear list of them,
  // and insert anchors at each one.
  let headingTags = postBody(headingSelector);
  for (let i=0; i<headingTags.length; i++) {
    let tag = headingTags[i];

    if (tagIsHeadingIfWholeParagraph(tag.tagName) && !tagIsWholeParagraph(tag)) {
      continue;
    }

    let title = cheerio(tag).text();

    if (title && title.trim()!=="") {
      let anchor = titleToAnchor(title, usedAnchors);
      usedAnchors[anchor] = true;
      cheerio(tag).attr("id", anchor);
      headings.push({
        title: title,
        anchor: anchor,
        level: tagToHeadingLevel(tag.tagName),
      });
    }
  }

  // Filter out unused heading levels, mapping the heading levels to consecutive
  // numbers starting from 1. So if a post uses
,
 and , those
  // will be levels 1, 2, and 3 (not 1, 3 and 7).

  // Get a list of heading levels used
  let headingLevelsUsedDict = {};
  for(let i=0; i<headings.length; i++)
    headingLevelsUsedDict[headings[i].level] = true;

  // Generate a mapping from raw heading levels to compressed heading levels
  let headingLevelsUsed = _.keys(headingLevelsUsedDict).sort();
  let headingLevelMap = {};
  for(let i=0; i<headingLevelsUsed.length; i++)
    headingLevelMap[ headingLevelsUsed[i] ] = i;

  // Mark sections with compressed heading levels
  for(let i=0; i<headings.length; i++)
    headings[i].level = headingLevelMap[headings[i].level]+1;

  if (headings.length) {
    headings.push({divider:true, level: 0, anchor: "postHeadingsDivider"})
  }
  return {
    html: postBody.html(),
    sections: headings,
    headingsCount: headings.length
  }
}

const reservedAnchorNames = ["top", "comments"];

// Given the text in a heading block and a dict of anchors that have been used
// in the post so far, generate an anchor, and return it. An anchor is a
// URL-safe string that can be used for within-document links, and which is
// not one of a few reserved anchor names.
function titleToAnchor(title, usedAnchors)
{
  let charsToUse = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
  let sb = [];

  for(let i=0; i<title.length; i++) {
    let ch = title.charAt(i);
    if(charsToUse.indexOf(ch) >= 0) {
      sb.push(ch);
    } else {
      sb.push('_');
    }
  }

  let anchor = sb.join('');
  if(!usedAnchors[anchor] && !_.find(reservedAnchorNames, x=>x===anchor))
    return anchor;

  let anchorSuffix = 1;
  while(usedAnchors[anchor + anchorSuffix])
    anchorSuffix++;
  return anchor+anchorSuffix;
}

// `<b>` and `<strong>` tags are headings iff they are the only thing in their
// paragraph. Return whether the given tag name is a tag with that property
// (ie, is `<strong>` or `<b>`).
function tagIsHeadingIfWholeParagraph(tagName)
{
  return tagName.toLowerCase() in headingIfWholeParagraph;
}

function tagIsWholeParagraph(tag) {
  if (!tag) return false;
  let parents = cheerio(tag).parent();
  if (!parents || !parents.length) return false;
  let parent = parents[0];
  if (parent.tagName.toLowerCase() !== 'p') return false;
  let selfAndSiblings = cheerio(parent).contents();
  if (selfAndSiblings.length != 1) return false;

  return true;
}

function tagToHeadingLevel(tagName)
{
  let lowerCaseTagName = tagName.toLowerCase();
  if (lowerCaseTagName in headingTags)
    return headingTags[lowerCaseTagName];
  else if (lowerCaseTagName in headingIfWholeParagraph)
    return headingIfWholeParagraph[lowerCaseTagName];
  else
    return 0;
}

async function getTocAnswers (document) {
  if (!document.question) return []

  let answersTerms = {
    answer:true,
    postId: document._id,
    deleted:false,
  }
  if (getSetting('forumType') === 'AlignmentForum') {
    answersTerms.af = true
  }
  const answers = await Comments.find(answersTerms, {sort:questionAnswersSort}).fetch()
  const answerSections = answers.map((answer) => ({
    title: `${answer.baseScore} ${answer.author}`,
    answer: answer,
    anchor: answer._id,
    level: 2
  }))

  if (answerSections.length) {
    return [{anchor: "answers", level:1, title:"Answers"}, ...answerSections]
  } else {
    return []
  }
}

async function getTocComments (document) {
  const commentSelector = {
    answer: false,
    parentAnswerId: null,
    postId: document._id

  }
  if (document.af && getSetting('forumType') === 'AlignmentForum') {
    commentSelector.af = true
  }
  const commentCount = await Comments.find(commentSelector).count()
  return [{anchor:"comments", level:0, title: Posts.getCommentCountStr(document, commentCount)}]
}

const getTableOfContentsData = async (document, args, options) => {
  const { html } = document.contents || {}
  const tableOfContents = extractTableOfContents(html) || []
  let tocSections = tableOfContents.headings
  const tocAnswers = await getTocAnswers(document)
  const tocComments = await getTocComments(document)

  tocSections.push(...tocAnswers)
  tocSections.push(...tocComments)

  if (tocSections.length >= MIN_HEADINGS_FOR_TOC) {
    return {
      html: tableOfContents.html,
      sections: tocSections,
      headingsCount: tocSections.length
    }
  }
}

Utils.getTableOfContentsData = getTableOfContentsData;
Utils.extractTableOfContents = extractTableOfContents;
<|endoftext|>