<commit_before>/*
Copyright 2012 - $Date $ by PeopleWare n.v.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

define(["dojo/_base/declare", "dojo/errors/CancelError", "dojo/_base/lang", "../log/logger!"],
  function(declare, CancelError, lang, logger) {

    return declare([], {
      // summary:
      //   Instances offer a function `guard(arg, promiseFunction)`
      //   that makes sure that promiseFunction is not called needlessly in parallel.

      // currentArg: any
      //   The last arg processed. Initially null.
      //   Changes if guard is called with another arg.
      currentArg: null,

      // processingPromise: Promise
      //   The Promise currently being processed for self.currentArg.
      processingPromise: null,

      guard: function(arg, /*Function*/ promiseFunction, /*Boolean?*/ reprocess) {
        // summary:
        //   Guards against calling promiseFunction in parallel multiple
        //   times for the same arg.
        // promiseFunction: Function
        //   promiseFunction is called with arg as argument, and returns a Promise
        // description:
        //   If guard is called with arg === self.currentArg, and there
        //   is a self.processingPromise, we return self.processingPromise.
        //   If there is no pending self.processingPromise, we restart
        //   promiseFunction, and return its result, if reprocess === true. Otherwise
        //   we return null.
        //   If guard is called with arg !== self.currentArg, and there is a
        //   a self.processingPromise, we cancel it. In any case, we start
        //   promiseFunction, and return its result;

        var self = this;

        function promiseFulfilled() {
          logger.debug("Promise for " + arg + " fulfilled. Forgetting the Promise");
          self.processingPromise = null;
        }

        function newPromise() {
          var newPromise = promiseFunction(arg);
          self.processingPromise = newPromise.then(
            function(promiseResult) {
              promiseFulfilled();
              return promiseResult;
            },
            function(err) {
              promiseFulfilled();
              throw err;
            }
          );
          /*
           Workaround for strange Store / QueryResult behavior.
           The query from a paging should add a "total" property to the result passed to QueryResult.
           In an async store, this is a promise for an array. If `promiseFunction` delivers such a Promise,
           it might have an added "total" property, which is also a Promise.
           By wrapping the Promise returned by `promiseFunction` in another Promise with `then` above, we hide
           that "total" property from the recipient.
           The code below makes it visible again.

           Furthermore, a Promise returned by `then` is frozen, so we cannot add it to the object directly.
           With a delegate, we can do this.
            */
          if (newPromise.total) {
            self.processingPromise = lang.delegate(
              self.processingPromise,
              {
                total: self.processingPromise.then(function() {
                  return newPromise.total; // which might be a Promise
                })
              }
            );
          }
          return self.processingPromise;
        }

        logger.trace("Request to process " + arg);
        if (arg === self.currentArg) {
          logger.trace("Requested arg is current arg.");
          if (self.processingPromise) {
            logger.debug("Currently processing arg. Returning current processing Promise.");
          }
          else if (reprocess) {
            logger.debug("Not currently processing arg. Starting reprocessing and returning reprocess Promise.");
            newPromise();
          }
          else {
            logger.debug("Not currently processing arg. No reprocessing requested. Returning null.");
          }
        }
        else {
          logger.debug("Requested arg is different from current arg. New call takes precedence.");
          self.currentArg = arg;
          if (self.processingPromise) {
            logger.debug("There is a pending Promise. Cancelling.");
            self.processingPromise.cancel(new CancelError("USER CANCELLED"));
          }
          logger.debug("Starting processing and returning process Promise.");
          newPromise();
        }
        return self.processingPromise;
      }

    });

  }
);
<commit_msg>Fix for major bug in Arbiter: * if the newPromise was immediately resolved, we never forgot it * if a Promise was cancelled, it's eventual resolution might make us forget the newer Promise<commit_after>/*
Copyright 2012 - $Date $ by PeopleWare n.v.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

define(["dojo/_base/declare", "dojo/errors/CancelError", "dojo/Deferred", "dojo/_base/lang", "../log/logger!"],
  function(declare, CancelError, Deferred, lang, logger) {

    return declare([], {
      // summary:
      //   Instances offer a function `guard(arg, promiseFunction)`
      //   that makes sure that promiseFunction is not called needlessly in parallel.

      // currentArg: any
      //   The last arg processed. Initially null.
      //   Changes if guard is called with another arg.
      currentArg: null,

      // processingPromise: Promise
      //   The Promise currently being processed for self.currentArg.
      processingPromise: null,

      guard: function(arg, /*Function*/ promiseFunction, /*Boolean?*/ reprocess) {
        // summary:
        //   Guards against calling promiseFunction in parallel multiple
        //   times for the same arg.
        // promiseFunction: Function
        //   promiseFunction is called with arg as argument, and returns a Promise
        // description:
        //   If guard is called with arg === self.currentArg, and there
        //   is a self.processingPromise, we return self.processingPromise.
        //   If there is no pending self.processingPromise, we restart
        //   promiseFunction, and return its result, if reprocess === true. Otherwise
        //   we return null.
        //   If guard is called with arg !== self.currentArg, and there is a
        //   a self.processingPromise, we cancel it. In any case, we start
        //   promiseFunction, and return its result;

        /* If the guard function is called again during this tick with the same
           arg, we should return the same Promise in all cases. This means we need
           to remember it at least until the end of this tick.
           If the guard function is called again during this tick with a different
           arg, we should cancel the first Promise, and create a new Promise. We should
           for the earlier Promise, and remember the new one.
           In that case, whether the fulfillment of the earlier Promise happens during
           this tick or later, it should not wipe our memory indiscriminately, because
           we now remember the newest Promise, not the earlier one being fulfilled.

           It is certainly an invariant that a new Promise will be remembered at least
           as long as it is not fulfilled (baring cancellation, but we can see
           cancellation as immediate). We may remember it longer. We cannot guarantee
           that the Promise that we remember is not fulfilled yet, for one because
           reacting to fulfillment happens in random order, and we might not be first.

           Forgetting a remembered Promise must happen on fulfillment.
           If a new Promise is fulfilled immediately, normal then-execution happens
           immediately. If we immediately "forget" the Promise, first we must take
           care that we do not remember it after we forgot it (bug in earlier version),
           but in general it makes not much sense to not at least remember it during
           this tick. A next call with the same argument cannot sensibly require a
           new request to the server. It might be interesting to pass out the resolved
           Promise as a result of a call, but on the other hand it does not hurt semantics
           to return a Promise that will not resolve but on the next tick (arguably a
           better semantics for a Promise in any case).

           It must be a postcondition that, when a returned Promise resolves, we have
           forgotten about it. Otherwise, it would be impossible or non-deterministic
           to do follow-up calls with the same argument in call-backs.

           The 2 together, the need to forget a Promise before we signal it resolved,
           and the need to remember a Promise we return at least during this tick,
           results in the need for the returned Promise never to be resolved during this
           tick. */
        /* IDEA another approach might be to remember the Promise also after it is
           resolved, and forget it on a call with another argument, or on a call with
           the same argument if reprocess is true and the Promise is fulfilled. This
           would however keep all Promise results in memory for an indefinite time. */

        var self = this;

        function newPromise() {
          logger.debug("Calling worker function (will return a Promise).");

          var newPromise = promiseFunction(arg);
          var deferred = new Deferred();
          var cleanedUpPromise = deferred.promise;

          function createCleanerAndFulfillerPromise() {

            function promiseFulfilled() {
              // summary:
              //   Forget the Promise we remember, if it is `promise`.

              logger.debug("Promise for " + arg + " fulfilled. Forgetting the Promise.");
              if (self.processingPromise === cleanedUpPromise) {
                logger.debug("The fullfilled promise is the one in memory. Forgetting it.");
                self.processingPromise = null;
              }
              else {
                logger.debug("The fullfiled promise is an old one. We forgot it already.");
              }
            }

            newPromise.then(
              function(promiseResult) {
                logger.debug("The worker function Promise resolved. Cleaning up.");
                promiseFulfilled();
                deferred.resolve(promiseResult);
                // Due to the semantics of then, promiseResult is never a Promise (in contrast
                // to what we needed to do in relent.js)
              },
              function(err) {
                logger.debug("The worker function Promise was rejected. Cleaning up. ", err);
                promiseFulfilled();
                deferred.reject(err);
              }
            );
          }

          /* If this Promise is already fulfilled, then would execute immediately,
             and that would make it try to forget the Promise before we remembered
             it. Furthermore, cleanedUpPromise is still undefined at that time.
             The Promise we will actually return and remember may only resolve
             during the next tick, even if the newPromise is resolved immediately. */
          if (newPromise.isFulfilled()) {
            logger.debug("The worker function Promise was immediately fulfilled. Waiting on next tick to resolve our result.");
            setTimeout(createCleanerAndFulfillerPromise, 0);
          }
          else {
            logger.debug("The worker function Promise was not immediately fulfilled. Will cleanup when it is.");
            /* if the newPromise is not fulfilled now, it will not be fulfilled during
               this tick. JavaScript is single-threaded. */
            createCleanerAndFulfillerPromise();
          }
          logger.debug("Worker function done. Will doctor returned Promise with total if needed, and return.");
           /*
             Workaround for strange Store / QueryResult behavior.
             The query from a paging should add a "total" property to the result passed to QueryResult.
             In an async store, this is a promise for an array. If `promiseFunction` delivers such a Promise,
             it might have an added "total" property, which is also a Promise.
             By wrapping the Promise returned by `promiseFunction` in another Promise with `then` above, we hide
             that "total" property from the recipient.
             The code below makes it visible again.

             Furthermore, a Promise returned by `then` is frozen, so we cannot add it to the object directly.
             With a delegate, we can do this.
           */
          if (newPromise.total) {
            cleanedUpPromise = lang.delegate(
              cleanedUpPromise,
              {
                total: cleanedUpPromise.then(function() {
                  return newPromise.total; // which might be a Promise
                })
              }
            );
          }

          self.processingPromise = cleanedUpPromise;
        }

        logger.trace("Request to process " + arg);
        if (arg === self.currentArg) {
          logger.trace("Requested arg is current arg.");
          if (self.processingPromise) {
            logger.debug("Currently processing arg. Returning current processing Promise.");
          }
          else if (reprocess) {
            logger.debug("Not currently processing arg. Starting reprocessing and returning reprocess Promise.");
            newPromise();
          }
          else {
            logger.debug("Not currently processing arg. No reprocessing requested. Returning null.");
          }
        }
        else {
          logger.debug("Requested arg is different from current arg. New call takes precedence.");
          self.currentArg = arg;
          if (self.processingPromise) {
            logger.debug("There is a pending Promise. Cancelling.");
            self.processingPromise.cancel(new CancelError("USER CANCELLED"));
            // self.processingPromise will be overridden by newPromise()
          }
          logger.debug("Starting processing and returning process Promise.");
          newPromise();
        }
        return self.processingPromise;
      }

    });

  }
);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The Lighthouse Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

/**
 * Logs messages via a UI butter.
 */
export class Logger {
  /**
   * @param {Element} element
   */
  constructor(element) {
    this.el = element;
    this._id = undefined;
  }

  /**
   * Shows a butter bar.
   * @param {string} msg The message to show.
   * @param {boolean=} autoHide True to hide the message after a duration.
   *     Default is true.
   */
  log(msg, autoHide = true) {
    this._id && clearTimeout(this._id);

    this.el.textContent = msg;
    this.el.classList.add('lh-show');
    if (autoHide) {
      this._id = setTimeout(() => {
        this.el.classList.remove('lh-show');
      }, 7000);
    }
  }

  /**
   * @param {string} msg
   */
  warn(msg) {
    this.log('Warning: ' + msg);
  }

  /**
   * @param {string} msg
   */
  error(msg) {
    this.log(msg);

    // Rethrow to make sure it's auditable as an error, but in a setTimeout so page
    // recovers gracefully and user can try loading a report again.
    setTimeout(() => {
      throw new Error(msg);
    }, 0);
  }

  /**
   * Explicitly hides the butter bar.
   */
  hide() {
    this._id && clearTimeout(this._id);
    this.el.classList.remove('show');
  }
}
<commit_msg>report: add lh- prefix to remaining show class (#13203)<commit_after>/**
 * @license
 * Copyright 2017 The Lighthouse Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

/**
 * Logs messages via a UI butter.
 */
export class Logger {
  /**
   * @param {Element} element
   */
  constructor(element) {
    this.el = element;
    this._id = undefined;
  }

  /**
   * Shows a butter bar.
   * @param {string} msg The message to show.
   * @param {boolean=} autoHide True to hide the message after a duration.
   *     Default is true.
   */
  log(msg, autoHide = true) {
    this._id && clearTimeout(this._id);

    this.el.textContent = msg;
    this.el.classList.add('lh-show');
    if (autoHide) {
      this._id = setTimeout(() => {
        this.el.classList.remove('lh-show');
      }, 7000);
    }
  }

  /**
   * @param {string} msg
   */
  warn(msg) {
    this.log('Warning: ' + msg);
  }

  /**
   * @param {string} msg
   */
  error(msg) {
    this.log(msg);

    // Rethrow to make sure it's auditable as an error, but in a setTimeout so page
    // recovers gracefully and user can try loading a report again.
    setTimeout(() => {
      throw new Error(msg);
    }, 0);
  }

  /**
   * Explicitly hides the butter bar.
   */
  hide() {
    this._id && clearTimeout(this._id);
    this.el.classList.remove('lh-show');
  }
}
<|endoftext|>


<commit_before>// Arranquem el Pong
var pong;
Game.ready(function() {
  pong = Game.start('game', Pong, {});
});

var tracker;
window.onload = function() {
  var video = document.getElementById('video');
  var canvas = document.getElementById('canvas');
  var context = canvas.getContext('2d');
  tracker = new tracking.ColorTracker();
  tracking.track('#video', tracker, { camera: true });
  tracker.on('track', function(event) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    event.data.forEach(function(rect) {
      context.strokeStyle = rect.color;
      context.strokeRect(rect.x, rect.y, rect.width, rect.height);
      context.font = '11px Helvetica';
      context.fillStyle = "#fff";
      //MOVE Y
      //console.log(pong.Defaults.height);
      console.log((rect.y*pong.Defaults.height)/canvas.height);
      context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
      context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
    });
  });
  tracker.setColors([]);
};

var colorActual = null;
function setColor(color){
  if (colorActual == color){
    tracker.setColors([]);
    colorActual = null;
  } else {
    tracker.setColors([color]);
    colorActual = color;
  }
}

//document.getElementById('yellow').addEventListener("click", myScript);
<commit_msg>Add phone controller<commit_after>// Arranquem el Pong
var pong;
Game.ready(function() {
  pong = Game.start('game', Pong, {});
});

var tracker;
var pl1_y = 0;
var next_move = 0;
window.onload = function() {
  var video = document.getElementById('video');
  var canvas = document.getElementById('canvas');
  var context = canvas.getContext('2d');
  tracker = new tracking.ColorTracker();
  tracking.track('#video', tracker, { camera: true });
  tracker.on('track', function(event) {
    context.clearRect(0, 0, canvas.width, canvas.height);
    event.data.forEach(function(rect) {
      context.strokeStyle = rect.color;
      context.strokeRect(rect.x, rect.y, rect.width, rect.height);
      context.font = '11px Helvetica';
      context.fillStyle = "#fff";
      //MOVE Y
      //console.log(pong.Defaults.height);
      next_move = ((rect.y*pong.Defaults.height)/canvas.height);
      console.log(next_move + "  " + pl1_y);
      pong.leftPaddle.setpos(pong.leftPaddle.x, next_move);

      pong.Paddle.up = (rect.y*pong.Defaults.height)/canvas.height;
      context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
      context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
    });
  });
  tracker.setColors([]);
};

var colorActual = null;
function setColor(color){
  if (colorActual == color){
    tracker.setColors([]);
    colorActual = null;
  } else {
    tracker.setColors([color]);
    colorActual = color;
  }
}

//document.getElementById('yellow').addEventListener("click", myScript);
<|endoftext|>


<commit_before>const MBContract          = require('./mb_contract');
const MBDisplayCurrencies = require('./mb_currency');
const MBDefaults          = require('./mb_defaults');
const MBTradingEvents     = require('./mb_event');
const MBPrice             = require('./mb_price');
const MBProcess           = require('./mb_process');
const cleanupChart        = require('../trade/charts/webtrader_chart').cleanupChart;
const BinarySocket        = require('../../base/socket');
const jpClient            = require('../../common/country_base').jpClient;
const JapanPortfolio      = require('../../japan/portfolio');
const localize            = require('../../../_common/localize').localize;
const State               = require('../../../_common/storage').State;

const MBTradePage = (() => {
    let events_initialized = 0;
    State.remove('is_mb_trading');

    const onLoad = () => {
        State.set('is_mb_trading', true);
        BinarySocket.wait('authorize').then(init);
    };

    const init = () => {
        if (jpClient()) {
            disableTrading();
            $('#panel').remove();
        } else {
            MBDefaults.set('disable_trading', 0);
            $('#ja-panel').remove();
        }

        if (events_initialized === 0) {
            events_initialized = 1;
            MBTradingEvents.init();
        }

        BinarySocket.send({ payout_currencies: 1 }).then(() => {
            MBDisplayCurrencies();
            MBProcess.getSymbols();
        });

        $('#tab_portfolio').find('a').text(localize('Portfolio'));
        $('#tab_graph').find('a').text(localize('Chart'));
        $('#tab_explanation').find('a').text(localize('Explanation'));
        State.set('is_chart_allowed', true);
        State.set('ViewPopup.onDisplayed', MBPrice.hidePriceOverlay);
        $('.container').css('max-width', '1200px');
    };

    const disableTrading = () => {
        MBDefaults.set('disable_trading', 1);
        $('#allow').removeClass('selected');
        $('#disallow').addClass('selected');
    };

    const reload = () => {
        window.location.reload();
    };

    const onUnload = () => {
        cleanupChart();
        State.set('is_chart_allowed', false);
        JapanPortfolio.hide();
        State.remove('is_mb_trading');
        events_initialized = 0;
        MBContract.onUnload();
        MBPrice.onUnload();
        MBProcess.onUnload();
        BinarySocket.clear('active_symbols');
        State.remove('ViewPopup.onDisplayed');
        $('.container').css('max-width', '');
    };

    const onDisconnect = () => {
        MBPrice.showPriceOverlay();
        cleanupChart();
        onLoad();
    };

    return {
        onLoad,
        reload,
        onUnload,
        onDisconnect,
    };
})();

module.exports = MBTradePage;
<commit_msg>redirect mlt to trading page<commit_after>const MBContract          = require('./mb_contract');
const MBDisplayCurrencies = require('./mb_currency');
const MBDefaults          = require('./mb_defaults');
const MBTradingEvents     = require('./mb_event');
const MBPrice             = require('./mb_price');
const MBProcess           = require('./mb_process');
const cleanupChart        = require('../trade/charts/webtrader_chart').cleanupChart;
const BinaryPjax          = require('../../base/binary_pjax');
const Client              = require('../../base/client');
const BinarySocket        = require('../../base/socket');
const jpClient            = require('../../common/country_base').jpClient;
const JapanPortfolio      = require('../../japan/portfolio');
const localize            = require('../../../_common/localize').localize;
const State               = require('../../../_common/storage').State;
const urlFor              = require('../../../_common/url').urlFor;

const MBTradePage = (() => {
    let events_initialized = 0;
    State.remove('is_mb_trading');

    const onLoad = () => {
        State.set('is_mb_trading', true);
        BinarySocket.wait('authorize').then(init);
    };

    const init = () => {
        if (Client.get('landing_company_shortcode') === 'malta') {
            BinaryPjax.load(urlFor('trading'));
            return;
        }
        if (jpClient()) {
            disableTrading();
            $('#panel').remove();
        } else {
            MBDefaults.set('disable_trading', 0);
            $('#ja-panel').remove();
        }

        if (events_initialized === 0) {
            events_initialized = 1;
            MBTradingEvents.init();
        }

        BinarySocket.send({ payout_currencies: 1 }).then(() => {
            MBDisplayCurrencies();
            MBProcess.getSymbols();
        });

        $('#tab_portfolio').find('a').text(localize('Portfolio'));
        $('#tab_graph').find('a').text(localize('Chart'));
        $('#tab_explanation').find('a').text(localize('Explanation'));
        State.set('is_chart_allowed', true);
        State.set('ViewPopup.onDisplayed', MBPrice.hidePriceOverlay);
        $('.container').css('max-width', '1200px');
    };

    const disableTrading = () => {
        MBDefaults.set('disable_trading', 1);
        $('#allow').removeClass('selected');
        $('#disallow').addClass('selected');
    };

    const reload = () => {
        window.location.reload();
    };

    const onUnload = () => {
        cleanupChart();
        State.set('is_chart_allowed', false);
        JapanPortfolio.hide();
        State.remove('is_mb_trading');
        events_initialized = 0;
        MBContract.onUnload();
        MBPrice.onUnload();
        MBProcess.onUnload();
        BinarySocket.clear('active_symbols');
        State.remove('ViewPopup.onDisplayed');
        $('.container').css('max-width', '');
    };

    const onDisconnect = () => {
        MBPrice.showPriceOverlay();
        cleanupChart();
        onLoad();
    };

    return {
        onLoad,
        reload,
        onUnload,
        onDisconnect,
    };
})();

module.exports = MBTradePage;
<|endoftext|>


<commit_before>'use strict';
const Base = require('yeoman-generator').Base;
const path = require('path');
const glob = require('glob').sync;
const Separator = require('inquirer').Separator;

const registry = require('../../knownPhoveaPlugins.json');
const knownPlugins = [].concat(registry.plugins, registry.splugins);
const knownPluginNames = [].concat(
  registry.plugins.map((d) => d.name),
  new Separator(),
  registry.splugins.map((d) => d.name));

function toRepository(plugin) {
  const p = knownPlugins[knownPluginNames.indexOf(plugin)];
  return p.repository;
}

function toSSH(repo) {
  if (/https:\/\/github.com/.test(repo)) {
    return `git@github.com:${repo.substring('https://github.com'.length)}`;
  }
  return repo;
}

function resolveNeighbors(plugins, useSSH) {
  var missing = [];
  const addMissing = (p) => {
    const config = require(path.resolve(p, '.yo-rc.json'))['generator-phovea'];
    const modules = [].concat(config.modules || [], config.smodules || []);
    this.log(`${p} => ${modules.join(' ')}`);
    missing.push(...modules.filter((m) => plugins.indexOf(m) < 0));
  };

  plugins.forEach(addMissing);

  while (missing.length > 0) {
    let next = missing.shift();
    let repo = toRepository(next);
    if (useSSH) {
      repo = toSSH(repo);
    }
    this.log(`git clone ${repo}`);
    this.spawnCommandSync('git', ['clone', repo], {
      cwd: this.destinationPath()
    });
    plugins.push(next);
    addMissing(next);
  }
}

function resolveAllNeighbors(useSSH) {
  const files = glob('*/.yo-rc.json', {
    cwd: this.destinationPath()
  });
  const plugins = files.map(path.dirname);
  return resolveNeighbors.call(this, plugins, useSSH);
}

class Generator extends Base {
  constructor(args, options) {
    super(args, options);

    this.option('ssh', {
      alias: 's',
      defaults: false,
      type: Boolean
    });
    this.option('recursive', {
      alias: 'r',
      defaults: false,
      type: Boolean
    });
    this.option('ueber', {
      alias: 'u',
      defaults: false,
      type: Boolean
    });
    this.argument('name', {
      required: false
    });
  }

  initializing() {
    this.props = {
      plugins: [],
      recursive: false,
      cloneSSH: false,
      runUeber: false
    };
  }

  prompting() {
    return this.prompt([{
      type: 'checkbox',
      name: 'plugins',
      message: 'Modules to clone',
      choices: knownPluginNames,
      default: this.args,
      when: !this.args.length
    }, {
      type: 'confirm',
      name: 'cloneSSH',
      message: 'SSH clone',
      store: true,
      default: this.options.ssh,
      when: !this.options.ssh
    }, {
      type: 'confirm',
      name: 'recursive',
      message: 'Recursive',
      default: this.options.recursive,
      when: !this.options.recursive
    }, {
      type: 'confirm',
      name: 'runUeber',
      message: 'Run Ueber',
      default: this.options.ueber,
      when: !this.options.ueber
    }]).then((props) => {
      this.props.plugins = props.plugins || this.args;
      this.props.cloneSSH = props.cloneSSH || this.options.ssh;
      this.props.recursive = props.recursive || this.options.recursive;
      this.props.runUeber = props.runUeber || this.options.ueber;
    });
  }

  default() {
    if (this.props.runUeber) {
      this.composeWith('phovea:ueber', {}, {
        local: require.resolve('../ueber')
      });
    }
  }

  writing() {
    var repos = this.props.plugins.map(toRepository);
    if (this.props.cloneSSH) {
      repos = repos.map(toSSH);
    }

    repos.forEach((repo) => {
      this.log(`git clone ${repo}`);
      this.spawnCommandSync('git', ['clone', repo], {
        cwd: this.destinationPath()
      });
    });
    if (this.props.recursive) {
      resolveNeighbors.call(this, this.props.plugins, this.props.cloneSSH);
    }
  }
}

module.exports = Generator;
module.exports.resolveNeighbors = resolveNeighbors;
module.exports.resolveAllNeighbors = resolveAllNeighbors;
<commit_msg>fix handling of separator in plugin names list<commit_after>'use strict';
const Base = require('yeoman-generator').Base;
const path = require('path');
const glob = require('glob').sync;
const Separator = require('inquirer').Separator;

const registry = require('../../knownPhoveaPlugins.json');
const knownPlugins = [].concat(registry.plugins, null, registry.splugins);
const knownPluginNames = [].concat(
  registry.plugins.map((d) => d.name),
  new Separator(),
  registry.splugins.map((d) => d.name));

function toRepository(plugin) {
  const p = knownPlugins[knownPluginNames.indexOf(plugin)];
  return p.repository;
}

function toSSH(repo) {
  if (/https:\/\/github.com/.test(repo)) {
    return `git@github.com:${repo.substring('https://github.com'.length)}`;
  }
  return repo;
}

function resolveNeighbors(plugins, useSSH) {
  var missing = [];
  const addMissing = (p) => {
    const config = require(path.resolve(p, '.yo-rc.json'))['generator-phovea'];
    const modules = [].concat(config.modules || [], config.smodules || []);
    this.log(`${p} => ${modules.join(' ')}`);
    missing.push(...modules.filter((m) => plugins.indexOf(m) < 0));
  };

  plugins.forEach(addMissing);

  while (missing.length > 0) {
    let next = missing.shift();
    let repo = toRepository(next);
    if (useSSH) {
      repo = toSSH(repo);
    }
    this.log(`git clone ${repo}`);
    this.spawnCommandSync('git', ['clone', repo], {
      cwd: this.destinationPath()
    });
    plugins.push(next);
    addMissing(next);
  }
}

function resolveAllNeighbors(useSSH) {
  const files = glob('*/.yo-rc.json', {
    cwd: this.destinationPath()
  });
  const plugins = files.map(path.dirname);
  return resolveNeighbors.call(this, plugins, useSSH);
}

class Generator extends Base {
  constructor(args, options) {
    super(args, options);

    this.option('ssh', {
      alias: 's',
      defaults: false,
      type: Boolean
    });
    this.option('recursive', {
      alias: 'r',
      defaults: false,
      type: Boolean
    });
    this.option('ueber', {
      alias: 'u',
      defaults: false,
      type: Boolean
    });
    this.argument('name', {
      required: false
    });
  }

  initializing() {
    this.props = {
      plugins: [],
      recursive: false,
      cloneSSH: false,
      runUeber: false
    };
  }

  prompting() {
    return this.prompt([{
      type: 'checkbox',
      name: 'plugins',
      message: 'Modules to clone',
      choices: knownPluginNames,
      default: this.args,
      when: !this.args.length
    }, {
      type: 'confirm',
      name: 'cloneSSH',
      message: 'SSH clone',
      store: true,
      default: this.options.ssh,
      when: !this.options.ssh
    }, {
      type: 'confirm',
      name: 'recursive',
      message: 'Recursive',
      default: this.options.recursive,
      when: !this.options.recursive
    }, {
      type: 'confirm',
      name: 'runUeber',
      message: 'Run Ueber',
      default: this.options.ueber,
      when: !this.options.ueber
    }]).then((props) => {
      this.props.plugins = props.plugins || this.args;
      this.props.cloneSSH = props.cloneSSH || this.options.ssh;
      this.props.recursive = props.recursive || this.options.recursive;
      this.props.runUeber = props.runUeber || this.options.ueber;
    });
  }

  default() {
    if (this.props.runUeber) {
      this.composeWith('phovea:ueber', {}, {
        local: require.resolve('../ueber')
      });
    }
  }

  writing() {
    var repos = this.props.plugins.map(toRepository);
    if (this.props.cloneSSH) {
      repos = repos.map(toSSH);
    }

    repos.forEach((repo) => {
      this.log(`git clone ${repo}`);
      this.spawnCommandSync('git', ['clone', repo], {
        cwd: this.destinationPath()
      });
    });
    if (this.props.recursive) {
      resolveNeighbors.call(this, this.props.plugins, this.props.cloneSSH);
    }
  }
}

module.exports = Generator;
module.exports.resolveNeighbors = resolveNeighbors;
module.exports.resolveAllNeighbors = resolveAllNeighbors;
<|endoftext|>


<commit_before> /*
 * AutoSuggest
 * Copyright 2009-2010 Drew Wilson
 * www.drewwilson.com
 * code.drewwilson.com/entry/autosuggest-jquery-plugin
 *
 * Version 1.4   -   Updated: Mar. 23, 2010
 *
 * This Plug-In will auto-complete or auto-suggest completed search queries
 * for you as you type. You can add multiple selections and remove them on
 * the fly. It supports keybord navigation (UP + DOWN + RETURN), as well
 * as multiple AutoSuggest fields on the same page.
 *
 * Inspied by the Autocomplete plugin by: Jörn Zaefferer
 * and the Facelist plugin by: Ian Tearle (iantearle.com)
 *
 * This AutoSuggest jQuery plug-in is dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($){
    $.fn.autoSuggest = function(data, options) {
        var defaults = {
            asHtmlID: false,
            startText: "Enter name here",
            emptyText: "No Results Found",
            preFill: {},
            limitText: "No More Selections Are Allowed",
            selectedItemProp: "value", //name of object property
            selectedValuesProp: "value", //name of object property
            searchObjProps: "value", //comma separated list of object property names
            queryParam: "q",
            retrieveLimit: false, //number for 'limit' param on ajax request
            extraParams: "",
            matchCase: false,
            minChars: 1,
            keyDelay: 400,
            resultsHighlight: true,
            neverSubmit: false,
            selectionLimit: false,
            showResultList: true,
            start: function(){},
            selectionClick: function(elem){},
            selectionAdded: function(elem){},
            selectionRemoved: function(elem){ elem.remove(); },
            formatList: false, //callback function
            beforeRetrieve: function(string){ return string; },
            retrieveComplete: function(data){ return data; },
            resultClick: function(data){},
            resultsComplete: function(){},
            source: false // function to take over processing the query
        };
        var opts = $.extend(defaults, options);

        var d_type = "object";
        var d_count = 0;
        if(typeof data == "string") {
            d_type = "string";
            var req_string = data;
        } else {
            var org_data = data;
            for (k in data) if (data.hasOwnProperty(k)) d_count++;
        }
        if((d_type == "object" && d_count > 0) || d_type == "string"){
            return this.each(function(x){
                if(!opts.asHtmlID){
                    x = x+""+Math.floor(Math.random()*100); //this ensures there will be unique IDs on the page if autoSuggest() is called multiple times
                    var x_id = "as-input-"+x;
                } else {
                    x = opts.asHtmlID;
                    var x_id = x;
                }
                opts.start.call(this);
                var input = $(this);
                input.attr("autocomplete","off").addClass("as-input").attr("id",x_id).val(opts.startText);
                var input_focus = false;

                // Setup basic elements and render them to the DOM
                input.wrap('

').wrap('

');
                var selections_holder = $("#as-selections-"+x);
                var org_li = $("#as-original-"+x);
                var results_holder = $('

').hide();
                var results_ul =  $('

');
                var values_input = $('<input type="hidden" class="as-values" name="as_values_'+x+'" id="as-values-'+x+'" />');
                var prefill_value = "";
                if(typeof opts.preFill == "string"){
                    var vals = opts.preFill.split(",");
                    for(var i=0; i < vals.length; i++){
                        var v_data = {};
                        v_data[opts.selectedValuesProp] = vals[i];
                        if(vals[i] != ""){
                            $.fn.autoSuggest.add_selected_item(v_data, "000"+i);
                        }
                    }
                    prefill_value = opts.preFill;
                } else {
                    prefill_value = "";
                    var prefill_count = 0;
                    for (k in opts.preFill) if (opts.preFill.hasOwnProperty(k)) prefill_count++;
                    if(prefill_count > 0){
                        for(var i=0; i < prefill_count; i++){
                            var new_v = opts.preFill[i][opts.selectedValuesProp];
                            if(new_v == undefined){ new_v = ""; }
                            prefill_value = prefill_value+new_v+",";
                            if(new_v != ""){
                                $.fn.autoSuggest.add_selected_item(opts.preFill[i], "000"+i);
                            }
                        }
                    }
                }
                if(prefill_value != ""){
                    input.val("");
                    var lastChar = prefill_value.substring(prefill_value.length-1);
                    if(lastChar != ","){ prefill_value = prefill_value+","; }
                    $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
                }
                values_input.val(","+prefill_value);
                input.after(values_input);
                selections_holder.click(function(){
                    input_focus = true;
                    input.focus();
                }).mousedown(function(){ input_focus = false; }).after(results_holder);

                var timeout = null;
                var prev = "";
                var totalSelections = 0;
                var tab_press = false;

                // Handle input field events
                input.focus(function(){
                    if($(this).val() == opts.startText && (values_input.val() == "" || values_input.val() === ",")) {
                        $(this).val("");
                    } else if(input_focus){
                        $("li.as-selection-item", selections_holder).removeClass("blur");
                        if($(this).val() != ""){
                            results_ul.css("width",selections_holder.outerWidth());
                            results_holder.show();
                        }
                    }
                    input_focus = true;
                    return true;
                }).blur(function(){
                    if($(this).val() == "" && values_input.val() == "" && prefill_value == ""){
                        $(this).val(opts.startText);
                    } else if(input_focus){
                        $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
                        results_holder.hide();
                    }
                }).keydown(function(e) {
                    // track last key pressed
                    lastKeyPressCode = e.keyCode;
                    first_focus = false;
                    switch(e.keyCode) {
                        case 38: // up
                            e.preventDefault();
                            moveSelection("up");
                            break;
                        case 40: // down
                            e.preventDefault();
                            moveSelection("down");
                            break;
                        case 8:  // delete
                            if(input.val() == ""){
                                var last = values_input.val().split(",");
                                last = last[last.length - 2];
                                selections_holder.children().not(org_li.prev()).removeClass("selected");
                                if(org_li.prev().hasClass("selected")){
                                    values_input.val(values_input.val().replace(last+",",""));
                                    opts.selectionRemoved.call(this, org_li.prev());
                                } else {
                                    opts.selectionClick.call(this, org_li.prev());
                                    org_li.prev().addClass("selected");
                                }
                            }
                            if(input.val().length == 1){
                                results_holder.hide();
                                 prev = "";
                            }
                            if($(":visible",results_holder).length > 0){
                                if (timeout){ clearTimeout(timeout); }
                                timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
                            }
                            break;
                        case 9: case 188:  // tab or comma
                            tab_press = false;
                            var active = $("li.active:first", results_holder);
                            if(active.length > 0){
                                active.click();
                            } else {
                                input.val('');
                            }
                            results_holder.hide();
                            if(opts.neverSubmit || active.length > 0){
                                e.preventDefault();
                            }
                            break;
                        case 13: // return
                            tab_press = false;
                            var active = $("li.active:first", results_holder);
                            if(active.length > 0){
                                active.click();
                            } else {
                                input.val('');
                            }
                            results_holder.hide();
                            if(opts.neverSubmit || active.length > 0){
                                e.preventDefault();
                            }
                            break;
                        default:
                            if(opts.showResultList){
                                if(opts.selectionLimit && $("li.as-selection-item", selections_holder).length >= opts.selectionLimit){
                                    results_ul.html('
'+opts.limitText+'
');
                                    results_holder.show();
                                } else {
                                    if (timeout){ clearTimeout(timeout); }
                                    timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
                                }
                            }
                            break;
                    }
                });

                function keyChange() {
                    // ignore if the following keys are pressed: [del] [shift] [capslock]
                    if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ){ return results_holder.hide(); }
                    var string = input.val().replace(/[\\]+|[\/]+/g,"");
                    if (string == prev) return;
                    prev = string;
                    if (string.length >= opts.minChars) {
                        selections_holder.addClass("loading");
                        if(d_type == "string"){
                            var limit = "";
                            if(opts.retrieveLimit){
                                limit = "&limit="+encodeURIComponent(opts.retrieveLimit);
                            }
                            if(opts.beforeRetrieve){
                                string = opts.beforeRetrieve.call(this, string);
                            }
                            if (typeof opts.source === "function") {
                                opts.source(string, function(data) {
                                    d_count = 0;
                                    var new_data = opts.retrieveComplete.call(this, data);
                                    for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++;
                                    processData(new_data, string);
                                });
                            } else {
                                $.getJSON(req_string+"?"+opts.queryParam+"="+encodeURIComponent(string)+limit+opts.extraParams, function(data){
                                    d_count = 0;
                                    var new_data = opts.retrieveComplete.call(this, data);
                                    for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++;
                                    processData(new_data, string);
                                });
                            }
                        } else {
                            if(opts.beforeRetrieve){
                                string = opts.beforeRetrieve.call(this, string);
                            }
                            processData(org_data, string);
                        }
                    } else {
                        selections_holder.removeClass("loading");
                        results_holder.hide();
                    }
                }
                var num_count = 0;
                function processData(data, query){
                    if (!opts.matchCase){ query = query.toLowerCase(); }
                    var matchCount = 0;
                    results_holder.html(results_ul.html("")).hide();
                    for(var i=0;i<d_count;i++){
                        var num = i;
                        num_count++;
                        var forward = false;
                        if(opts.searchObjProps == "value") {
                            var str = data[num].value;
                        } else {
                            var str = "";
                            var names = opts.searchObjProps.split(",");
                            for(var y=0;y<names.length;y++){
                                var name = $.trim(names[y]);
                                str = str+data[num][name]+" ";
                            }
                        }
                        if(str){
                            if (!opts.matchCase){ str = str.toLowerCase(); }
                            query = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); // http://simonwillison.net/2006/Jan/20/escape/
                            if(str.search(query) != -1 && values_input.val().search(data[num][opts.selectedValuesProp]+",") == -1){
                                forward = true;
                            }
                        }
                        if(forward){
                            var formatted = $('

').click(function(){
                                    var raw_data = $(this).data("data");
                                    var number = raw_data.num;
                                    if($("#as-selection-"+number, selections_holder).length <= 0 && !tab_press){
                                        var data = raw_data.attributes;
                                        input.val("").focus();
                                        prev = "";
                                        $.fn.autoSuggest.add_selected_item(data, number);
                                        opts.resultClick.call(this, raw_data);
                                        $("li", results_ul).removeClass("active");
                                        results_holder.hide();
                                    }
                                    tab_press = false;
                                }).mousedown(function(){ input_focus = false; }).mouseover(function(){
                                    $("li", results_ul).removeClass("active");
                                    $(this).addClass("active");
                                }).data("data",{attributes: data[num], num: num_count});
                            var this_data = $.extend({},data[num]);
                            if (!opts.matchCase){
                                var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi");
                            } else {
                                var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "g");
                            }

                            if(opts.resultsHighlight){
                                this_data[opts.selectedItemProp] = this_data[opts.selectedItemProp].replace(regx,"$1");
                            }
                            if(!opts.formatList){
                                formatted = formatted.html(this_data[opts.selectedItemProp]);
                            } else {
                                formatted = opts.formatList.call(this, this_data, formatted);
                            }
                            results_ul.append(formatted);
                            delete this_data;
                            matchCount++;
                            if(opts.retrieveLimit && opts.retrieveLimit == matchCount ){ break; }
                        }
                    }
                    selections_holder.removeClass("loading");
                    if(matchCount <= 0){
                        results_ul.html('
'+opts.emptyText+'
');
                    }
                    results_ul.css("width", selections_holder.outerWidth());
                    results_holder.show();
                    opts.resultsComplete.call(this);
                }

                $.fn.autoSuggest.add_selected_item = function(data, num){
                    values_input.val(values_input.val()+data[opts.selectedValuesProp]+",");
                    var item = $('

').click(function(){
                            opts.selectionClick.call(this, $(this));
                            selections_holder.children().removeClass("selected");
                            $(this).addClass("selected");
                        }).mousedown(function(){ input_focus = false; });
                    var close = $('×').click(function(){
                            values_input.val(values_input.val().replace(data[opts.selectedValuesProp]+",",""));
                            opts.selectionRemoved.call(this, item);
                            input_focus = true;
                            input.focus();
                            return false;
                        });
                    org_li.before(item.html(data[opts.selectedItemProp]).prepend(close));
                    opts.selectionAdded.call(this, org_li.prev());
                }

                function moveSelection(direction){
                    if($(":visible",results_holder).length > 0){
                        var lis = $("li", results_holder);
                        if(direction == "down"){
                            var start = lis.eq(0);
                        } else {
                            var start = lis.filter(":last");
                        }
                        var active = $("li.active:first", results_holder);
                        if(active.length > 0){
                            if(direction == "down"){
                            start = active.next();
                            } else {
                                start = active.prev();
                            }
                        }
                        lis.removeClass("active");
                        start.addClass("active");
                    }
                }

            });
        }
    }
})(jQuery);<commit_msg>undoing change to autoSuggest default, instead passing in our own in the file we call it in<commit_after> /*
 * AutoSuggest
 * Copyright 2009-2010 Drew Wilson
 * www.drewwilson.com
 * code.drewwilson.com/entry/autosuggest-jquery-plugin
 *
 * Version 1.4   -   Updated: Mar. 23, 2010
 *
 * This Plug-In will auto-complete or auto-suggest completed search queries
 * for you as you type. You can add multiple selections and remove them on
 * the fly. It supports keybord navigation (UP + DOWN + RETURN), as well
 * as multiple AutoSuggest fields on the same page.
 *
 * Inspied by the Autocomplete plugin by: Jörn Zaefferer
 * and the Facelist plugin by: Ian Tearle (iantearle.com)
 *
 * This AutoSuggest jQuery plug-in is dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($){
    $.fn.autoSuggest = function(data, options) {
        var defaults = {
            asHtmlID: false,
            startText: "Enter Name Here",
            emptyText: "No Results Found",
            preFill: {},
            limitText: "No More Selections Are Allowed",
            selectedItemProp: "value", //name of object property
            selectedValuesProp: "value", //name of object property
            searchObjProps: "value", //comma separated list of object property names
            queryParam: "q",
            retrieveLimit: false, //number for 'limit' param on ajax request
            extraParams: "",
            matchCase: false,
            minChars: 1,
            keyDelay: 400,
            resultsHighlight: true,
            neverSubmit: false,
            selectionLimit: false,
            showResultList: true,
            start: function(){},
            selectionClick: function(elem){},
            selectionAdded: function(elem){},
            selectionRemoved: function(elem){ elem.remove(); },
            formatList: false, //callback function
            beforeRetrieve: function(string){ return string; },
            retrieveComplete: function(data){ return data; },
            resultClick: function(data){},
            resultsComplete: function(){},
            source: false // function to take over processing the query
        };
        var opts = $.extend(defaults, options);

        var d_type = "object";
        var d_count = 0;
        if(typeof data == "string") {
            d_type = "string";
            var req_string = data;
        } else {
            var org_data = data;
            for (k in data) if (data.hasOwnProperty(k)) d_count++;
        }
        if((d_type == "object" && d_count > 0) || d_type == "string"){
            return this.each(function(x){
                if(!opts.asHtmlID){
                    x = x+""+Math.floor(Math.random()*100); //this ensures there will be unique IDs on the page if autoSuggest() is called multiple times
                    var x_id = "as-input-"+x;
                } else {
                    x = opts.asHtmlID;
                    var x_id = x;
                }
                opts.start.call(this);
                var input = $(this);
                input.attr("autocomplete","off").addClass("as-input").attr("id",x_id).val(opts.startText);
                var input_focus = false;

                // Setup basic elements and render them to the DOM
                input.wrap('

').wrap('

');
                var selections_holder = $("#as-selections-"+x);
                var org_li = $("#as-original-"+x);
                var results_holder = $('

').hide();
                var results_ul =  $('

');
                var values_input = $('<input type="hidden" class="as-values" name="as_values_'+x+'" id="as-values-'+x+'" />');
                var prefill_value = "";
                if(typeof opts.preFill == "string"){
                    var vals = opts.preFill.split(",");
                    for(var i=0; i < vals.length; i++){
                        var v_data = {};
                        v_data[opts.selectedValuesProp] = vals[i];
                        if(vals[i] != ""){
                            $.fn.autoSuggest.add_selected_item(v_data, "000"+i);
                        }
                    }
                    prefill_value = opts.preFill;
                } else {
                    prefill_value = "";
                    var prefill_count = 0;
                    for (k in opts.preFill) if (opts.preFill.hasOwnProperty(k)) prefill_count++;
                    if(prefill_count > 0){
                        for(var i=0; i < prefill_count; i++){
                            var new_v = opts.preFill[i][opts.selectedValuesProp];
                            if(new_v == undefined){ new_v = ""; }
                            prefill_value = prefill_value+new_v+",";
                            if(new_v != ""){
                                $.fn.autoSuggest.add_selected_item(opts.preFill[i], "000"+i);
                            }
                        }
                    }
                }
                if(prefill_value != ""){
                    input.val("");
                    var lastChar = prefill_value.substring(prefill_value.length-1);
                    if(lastChar != ","){ prefill_value = prefill_value+","; }
                    $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
                }
                values_input.val(","+prefill_value);
                input.after(values_input);
                selections_holder.click(function(){
                    input_focus = true;
                    input.focus();
                }).mousedown(function(){ input_focus = false; }).after(results_holder);

                var timeout = null;
                var prev = "";
                var totalSelections = 0;
                var tab_press = false;

                // Handle input field events
                input.focus(function(){
                    if($(this).val() == opts.startText && (values_input.val() == "" || values_input.val() === ",")) {
                        $(this).val("");
                    } else if(input_focus){
                        $("li.as-selection-item", selections_holder).removeClass("blur");
                        if($(this).val() != ""){
                            results_ul.css("width",selections_holder.outerWidth());
                            results_holder.show();
                        }
                    }
                    input_focus = true;
                    return true;
                }).blur(function(){
                    if($(this).val() == "" && values_input.val() == "" && prefill_value == ""){
                        $(this).val(opts.startText);
                    } else if(input_focus){
                        $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
                        results_holder.hide();
                    }
                }).keydown(function(e) {
                    // track last key pressed
                    lastKeyPressCode = e.keyCode;
                    first_focus = false;
                    switch(e.keyCode) {
                        case 38: // up
                            e.preventDefault();
                            moveSelection("up");
                            break;
                        case 40: // down
                            e.preventDefault();
                            moveSelection("down");
                            break;
                        case 8:  // delete
                            if(input.val() == ""){
                                var last = values_input.val().split(",");
                                last = last[last.length - 2];
                                selections_holder.children().not(org_li.prev()).removeClass("selected");
                                if(org_li.prev().hasClass("selected")){
                                    values_input.val(values_input.val().replace(last+",",""));
                                    opts.selectionRemoved.call(this, org_li.prev());
                                } else {
                                    opts.selectionClick.call(this, org_li.prev());
                                    org_li.prev().addClass("selected");
                                }
                            }
                            if(input.val().length == 1){
                                results_holder.hide();
                                 prev = "";
                            }
                            if($(":visible",results_holder).length > 0){
                                if (timeout){ clearTimeout(timeout); }
                                timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
                            }
                            break;
                        case 9: case 188:  // tab or comma
                            tab_press = false;
                            var active = $("li.active:first", results_holder);
                            if(active.length > 0){
                                active.click();
                            } else {
                                input.val('');
                            }
                            results_holder.hide();
                            if(opts.neverSubmit || active.length > 0){
                                e.preventDefault();
                            }
                            break;
                        case 13: // return
                            tab_press = false;
                            var active = $("li.active:first", results_holder);
                            if(active.length > 0){
                                active.click();
                            } else {
                                input.val('');
                            }
                            results_holder.hide();
                            if(opts.neverSubmit || active.length > 0){
                                e.preventDefault();
                            }
                            break;
                        default:
                            if(opts.showResultList){
                                if(opts.selectionLimit && $("li.as-selection-item", selections_holder).length >= opts.selectionLimit){
                                    results_ul.html('
'+opts.limitText+'
');
                                    results_holder.show();
                                } else {
                                    if (timeout){ clearTimeout(timeout); }
                                    timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
                                }
                            }
                            break;
                    }
                });

                function keyChange() {
                    // ignore if the following keys are pressed: [del] [shift] [capslock]
                    if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ){ return results_holder.hide(); }
                    var string = input.val().replace(/[\\]+|[\/]+/g,"");
                    if (string == prev) return;
                    prev = string;
                    if (string.length >= opts.minChars) {
                        selections_holder.addClass("loading");
                        if(d_type == "string"){
                            var limit = "";
                            if(opts.retrieveLimit){
                                limit = "&limit="+encodeURIComponent(opts.retrieveLimit);
                            }
                            if(opts.beforeRetrieve){
                                string = opts.beforeRetrieve.call(this, string);
                            }
                            if (typeof opts.source === "function") {
                                opts.source(string, function(data) {
                                    d_count = 0;
                                    var new_data = opts.retrieveComplete.call(this, data);
                                    for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++;
                                    processData(new_data, string);
                                });
                            } else {
                                $.getJSON(req_string+"?"+opts.queryParam+"="+encodeURIComponent(string)+limit+opts.extraParams, function(data){
                                    d_count = 0;
                                    var new_data = opts.retrieveComplete.call(this, data);
                                    for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++;
                                    processData(new_data, string);
                                });
                            }
                        } else {
                            if(opts.beforeRetrieve){
                                string = opts.beforeRetrieve.call(this, string);
                            }
                            processData(org_data, string);
                        }
                    } else {
                        selections_holder.removeClass("loading");
                        results_holder.hide();
                    }
                }
                var num_count = 0;
                function processData(data, query){
                    if (!opts.matchCase){ query = query.toLowerCase(); }
                    var matchCount = 0;
                    results_holder.html(results_ul.html("")).hide();
                    for(var i=0;i<d_count;i++){
                        var num = i;
                        num_count++;
                        var forward = false;
                        if(opts.searchObjProps == "value") {
                            var str = data[num].value;
                        } else {
                            var str = "";
                            var names = opts.searchObjProps.split(",");
                            for(var y=0;y<names.length;y++){
                                var name = $.trim(names[y]);
                                str = str+data[num][name]+" ";
                            }
                        }
                        if(str){
                            if (!opts.matchCase){ str = str.toLowerCase(); }
                            query = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); // http://simonwillison.net/2006/Jan/20/escape/
                            if(str.search(query) != -1 && values_input.val().search(data[num][opts.selectedValuesProp]+",") == -1){
                                forward = true;
                            }
                        }
                        if(forward){
                            var formatted = $('

').click(function(){
                                    var raw_data = $(this).data("data");
                                    var number = raw_data.num;
                                    if($("#as-selection-"+number, selections_holder).length <= 0 && !tab_press){
                                        var data = raw_data.attributes;
                                        input.val("").focus();
                                        prev = "";
                                        $.fn.autoSuggest.add_selected_item(data, number);
                                        opts.resultClick.call(this, raw_data);
                                        $("li", results_ul).removeClass("active");
                                        results_holder.hide();
                                    }
                                    tab_press = false;
                                }).mousedown(function(){ input_focus = false; }).mouseover(function(){
                                    $("li", results_ul).removeClass("active");
                                    $(this).addClass("active");
                                }).data("data",{attributes: data[num], num: num_count});
                            var this_data = $.extend({},data[num]);
                            if (!opts.matchCase){
                                var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi");
                            } else {
                                var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "g");
                            }

                            if(opts.resultsHighlight){
                                this_data[opts.selectedItemProp] = this_data[opts.selectedItemProp].replace(regx,"$1");
                            }
                            if(!opts.formatList){
                                formatted = formatted.html(this_data[opts.selectedItemProp]);
                            } else {
                                formatted = opts.formatList.call(this, this_data, formatted);
                            }
                            results_ul.append(formatted);
                            delete this_data;
                            matchCount++;
                            if(opts.retrieveLimit && opts.retrieveLimit == matchCount ){ break; }
                        }
                    }
                    selections_holder.removeClass("loading");
                    if(matchCount <= 0){
                        results_ul.html('
'+opts.emptyText+'
');
                    }
                    results_ul.css("width", selections_holder.outerWidth());
                    results_holder.show();
                    opts.resultsComplete.call(this);
                }

                $.fn.autoSuggest.add_selected_item = function(data, num){
                    values_input.val(values_input.val()+data[opts.selectedValuesProp]+",");
                    var item = $('

').click(function(){
                            opts.selectionClick.call(this, $(this));
                            selections_holder.children().removeClass("selected");
                            $(this).addClass("selected");
                        }).mousedown(function(){ input_focus = false; });
                    var close = $('×').click(function(){
                            values_input.val(values_input.val().replace(data[opts.selectedValuesProp]+",",""));
                            opts.selectionRemoved.call(this, item);
                            input_focus = true;
                            input.focus();
                            return false;
                        });
                    org_li.before(item.html(data[opts.selectedItemProp]).prepend(close));
                    opts.selectionAdded.call(this, org_li.prev());
                }

                function moveSelection(direction){
                    if($(":visible",results_holder).length > 0){
                        var lis = $("li", results_holder);
                        if(direction == "down"){
                            var start = lis.eq(0);
                        } else {
                            var start = lis.filter(":last");
                        }
                        var active = $("li.active:first", results_holder);
                        if(active.length > 0){
                            if(direction == "down"){
                            start = active.next();
                            } else {
                                start = active.prev();
                            }
                        }
                        lis.removeClass("active");
                        start.addClass("active");
                    }
                }

            });
        }
    }
})(jQuery);<|endoftext|>


<commit_before>'use strict';

/**
 * Trello connector.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var async = require('async');
var https = require('https');
var testing = require('testing');
var credentials = require('../credentials.json');

// globals
var log = new Log('info');

// constants

exports.Board = function(options)
{
	// self-reference
	var self = this;

	self.createCard = function(name, description, listId, callback)
	{
		var variables = {
			name: name,
			desc: description,
			labels: 'orange',
		};
		var path = '/1/lists/' + listId + '/cards';
		makeRequest(path, 'POST', variables, callback);
	};

	function makeRequest(path, method, variables, callback)
	{
		variables.token = options.token;
		variables.key = options.key;
		var params = {
			host: 'trello.com',
			path: path + '?' + convert(variables),
			method: method,
			agent: false,
		};
		var request = https.request(params, function(response)
		{
			if (response.statusCode != 200)
			{
				return callback('Invalid response for ' + params.path + ': ' + response.statusCode);
			}
			var body = '';
			response.on('data', function(data)
			{
				body += String(data);
			});
			response.on('error', function(error)
			{
				callback('Could not listen to response: ' + error);
			});
			response.on('end', function()
			{
				var json;
				try
				{
					json = JSON.parse(body);
				}
				catch(exception)
				{
					return callback('Could not parse JSON: ' + exception);
				}
				return callback(null, json);
			});
		});
		request.end();
		request.on('error', function(error)
		{
			callback('Could not connect to ' + path + ': ' + error);
		});
	}

	function convert(map)
	{
		var results = [];
		for (var key in map)
		{
			results.push(key + '=' + encodeURIComponent(map[key]));
		}
		return results.join('&');
	}

	self.readList = function(listId, callback)
	{
		var path = '/1/lists/' + listId;
		makeRequest(path, 'GET', {cards: 'all'}, callback);
	};

	self.findCard = function(name, callback)
	{
		readBoard(function(error, board)
		{
			if (error)
			{
				return callback(error);
			}
			var tasks = board.lists.map(function(list)
			{
				return function(callback)
				{
					self.readList(list.id, callback);
				};
			});
			async.series(tasks, function(error, lists)
			{
				if (error)
				{
					return callback(error);
				}
				var matchingCards = lists.map(function(list)
				{
					var matchingInList = list.cards.filter(function(card)
					{
						return card.name.startsWith(name);
					});
					if (matchingInList.length)
					{
						return matchingInList[0];
					}
					return null;
				}).filter(function(matchingCard)
				{
					return matchingCard;
				});
				return callback(null, matchingCards[0]);
			});
		});
	};

	/**
	 * Returns an array of comments for a card, including text and author.
	 */
	self.readComments = function(cardId, callback)
	{
		var path = '/1/cards/' + cardId + '/actions';
		makeRequest(path, 'GET', {filter: 'commentCard'}, function(error, results)
		{
			if (error)
			{
				return error;
			}
			var comments = results.map(function(commentCard)
			{
				return {
					text: commentCard.data.text,
					date: commentCard.date,
					author: commentCard.memberCreator,
				};
			});
			callback(null, comments);
		});
	};

	self.moveCard = function(title, listId, callback)
	{
		if (title.length == 24 && !title.contains('['))
		{
			return moveCardId(title, listId, callback);
		}
		self.findCard(title, function(error, card)
		{
			if (error)
			{
				return callback(error);
			}
			if (!card)
			{
				return callback('Could not find card ' + title);
			}
			moveCardId(card.id, listId, callback);
		});
	};

	function moveCardId(cardId, listId, callback)
	{
		var path = '/1/cards/' + cardId + '/idList';
		makeRequest(path, 'PUT', {value: listId}, callback);
	}

	function readBoard(callback)
	{
		var path = '/1/board/' + options.board;
		makeRequest(path, 'GET', {lists: 'all'}, callback);
	}

	self.renameCard = function(cardId, name, callback)
	{
		var path = '/1/cards/' + cardId;
		makeRequest(path, 'PUT', {name: name}, callback);
	};

	self.addComment = function(cardId, text, callback)
	{
		var variables = {text: text};
		var path = '/1/cards/' + cardId + '/actions/comments';
		makeRequest(path, 'POST', variables, callback);
	};
};

function testCreateMoveCard(callback)
{
	var board = new exports.Board(credentials);
	var description = 'This is a sample card.\n\nIn no way related to this [repo]' +
		'(https://github.com/alexfernandez/trello-reviewer).\n\nEnjoy!';
	board.createCard('name', description, credentials.inProgress, function(error)
	{
		testing.check(error, 'Could not create card', callback);
		board.moveCard('name', credentials.cancelled, function(error)
		{
			testing.check(error, 'Could not move card', callback);
			testing.success(callback);
		});
	});
}

function testReadList(callback)
{
	var board = new exports.Board(credentials);
	board.readList(credentials.inProgress, function(error, list)
	{
		testing.check(error, 'Could not read cards', callback);
		testing.assert(list.cards.length, 'No cards', callback);
		testing.success(callback);
	});
}

function testFindCard(callback)
{
	var board = new exports.Board(credentials);
	board.findCard('name', function(error, card)
	{
		testing.check(error, 'Could not find card', callback);
		testing.assert(card.name == 'name', 'Invalid card name', callback);
		board.renameCard(card.id, 'newName', function(error)
		{
			testing.check(error, 'Could not rename card', callback);
			testing.success(callback);
		});
	});
}

function testAddReadComment(callback)
{
	var board = new exports.Board(credentials);
	board.readList(credentials.inProgress, function(error, list)
	{
		testing.check(error, 'Could not read list', callback);
		testing.assert(list.cards.length, 'No cards in inProgress', callback);
		var cardId = list.cards[0].id;
		var then = Date.now();
		var text = 'Test comment';
		board.addComment(cardId, text, function(error)
		{
			testing.check(error, 'Could not add comment', callback);
			board.readComments(cardId, function(error, comments)
			{
				testing.check(error, 'Could not read comments', callback);
				testing.assert(comments.length >= 1, 'Not enough comments', callback);
				testing.assertEquals(comments[0].text, text, 'Invalid comment text', callback);
				var date = new Date(comments[0].date);
				var elapsed = date.getTime() - then;
				testing.assert(elapsed < 10000, 'Too much time since last comment', callback);
				testing.success(callback);
			});
		});
	});

}

/**
 * Run the tests.
 */
exports.test = function(callback)
{
	testing.run([
		testCreateMoveCard,
		testReadList,
		testFindCard,
		testAddReadComment,
	], 50000, callback);
};

// start server if invoked directly
if (__filename == process.argv[1])
{
	log = new Log('debug');
	exports.test(testing.show);
}

<commit_msg>Ignore closed cards.<commit_after>'use strict';

/**
 * Trello connector.
 * (C) 2015 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var async = require('async');
var https = require('https');
var testing = require('testing');
var credentials = require('../credentials.json');

// globals
var log = new Log('info');

// constants

exports.Board = function(options)
{
	// self-reference
	var self = this;

	self.createCard = function(name, description, listId, callback)
	{
		var variables = {
			name: name,
			desc: description,
			labels: 'orange',
		};
		var path = '/1/lists/' + listId + '/cards';
		makeRequest(path, 'POST', variables, callback);
	};

	function makeRequest(path, method, variables, callback)
	{
		variables.token = options.token;
		variables.key = options.key;
		var params = {
			host: 'trello.com',
			path: path + '?' + convert(variables),
			method: method,
			agent: false,
		};
		var request = https.request(params, function(response)
		{
			if (response.statusCode != 200)
			{
				return callback('Invalid response for ' + params.path + ': ' + response.statusCode);
			}
			var body = '';
			response.on('data', function(data)
			{
				body += String(data);
			});
			response.on('error', function(error)
			{
				callback('Could not listen to response: ' + error);
			});
			response.on('end', function()
			{
				var json;
				try
				{
					json = JSON.parse(body);
				}
				catch(exception)
				{
					return callback('Could not parse JSON: ' + exception);
				}
				return callback(null, json);
			});
		});
		request.end();
		request.on('error', function(error)
		{
			callback('Could not connect to ' + path + ': ' + error);
		});
	}

	function convert(map)
	{
		var results = [];
		for (var key in map)
		{
			results.push(key + '=' + encodeURIComponent(map[key]));
		}
		return results.join('&');
	}

	self.readList = function(listId, callback)
	{
		var path = '/1/lists/' + listId;
		makeRequest(path, 'GET', {cards: 'all'}, function(error, list)
		{
			if (error)
			{
				return callback(error);
			}
			list.cards.filter(function(card)
			{
				return card.closed;
			});
			return callback(null, list);
		});
	};

	self.findCard = function(name, callback)
	{
		readBoard(function(error, board)
		{
			if (error)
			{
				return callback(error);
			}
			var tasks = board.lists.map(function(list)
			{
				return function(callback)
				{
					self.readList(list.id, callback);
				};
			});
			async.series(tasks, function(error, lists)
			{
				if (error)
				{
					return callback(error);
				}
				var matchingCards = lists.map(function(list)
				{
					var matchingInList = list.cards.filter(function(card)
					{
						return card.name.startsWith(name);
					});
					if (matchingInList.length)
					{
						return matchingInList[0];
					}
					return null;
				}).filter(function(matchingCard)
				{
					return matchingCard;
				});
				return callback(null, matchingCards[0]);
			});
		});
	};

	/**
	 * Returns an array of comments for a card, including text and author.
	 */
	self.readComments = function(cardId, callback)
	{
		var path = '/1/cards/' + cardId + '/actions';
		makeRequest(path, 'GET', {filter: 'commentCard'}, function(error, results)
		{
			if (error)
			{
				return error;
			}
			var comments = results.map(function(commentCard)
			{
				return {
					text: commentCard.data.text,
					date: commentCard.date,
					author: commentCard.memberCreator,
				};
			});
			callback(null, comments);
		});
	};

	self.moveCard = function(title, listId, callback)
	{
		if (title.length == 24 && !title.contains('['))
		{
			return moveCardId(title, listId, callback);
		}
		self.findCard(title, function(error, card)
		{
			if (error)
			{
				return callback(error);
			}
			if (!card)
			{
				return callback('Could not find card ' + title);
			}
			moveCardId(card.id, listId, callback);
		});
	};

	function moveCardId(cardId, listId, callback)
	{
		var path = '/1/cards/' + cardId + '/idList';
		makeRequest(path, 'PUT', {value: listId}, callback);
	}

	function readBoard(callback)
	{
		var path = '/1/board/' + options.board;
		makeRequest(path, 'GET', {lists: 'all'}, callback);
	}

	self.renameCard = function(cardId, name, callback)
	{
		var path = '/1/cards/' + cardId;
		makeRequest(path, 'PUT', {name: name}, callback);
	};

	self.addComment = function(cardId, text, callback)
	{
		var variables = {text: text};
		var path = '/1/cards/' + cardId + '/actions/comments';
		makeRequest(path, 'POST', variables, callback);
	};
};

function testCreateMoveCard(callback)
{
	var board = new exports.Board(credentials);
	var description = 'This is a sample card.\n\nIn no way related to this [repo]' +
		'(https://github.com/alexfernandez/trello-reviewer).\n\nEnjoy!';
	board.createCard('name', description, credentials.inProgress, function(error)
	{
		testing.check(error, 'Could not create card', callback);
		board.moveCard('name', credentials.cancelled, function(error)
		{
			testing.check(error, 'Could not move card', callback);
			testing.success(callback);
		});
	});
}

function testReadList(callback)
{
	var board = new exports.Board(credentials);
	board.readList(credentials.inProgress, function(error, list)
	{
		testing.check(error, 'Could not read cards', callback);
		testing.assert(list.cards.length, 'No cards', callback);
		testing.success(callback);
	});
}

function testFindCard(callback)
{
	var board = new exports.Board(credentials);
	board.findCard('name', function(error, card)
	{
		testing.check(error, 'Could not find card', callback);
		testing.assert(card.name == 'name', 'Invalid card name', callback);
		board.renameCard(card.id, 'newName', function(error)
		{
			testing.check(error, 'Could not rename card', callback);
			testing.success(callback);
		});
	});
}

function testAddReadComment(callback)
{
	var board = new exports.Board(credentials);
	board.readList(credentials.inProgress, function(error, list)
	{
		testing.check(error, 'Could not read list', callback);
		testing.assert(list.cards.length, 'No cards in inProgress', callback);
		var cardId = list.cards[0].id;
		var then = Date.now();
		var text = 'Test comment';
		board.addComment(cardId, text, function(error)
		{
			testing.check(error, 'Could not add comment', callback);
			board.readComments(cardId, function(error, comments)
			{
				testing.check(error, 'Could not read comments', callback);
				testing.assert(comments.length >= 1, 'Not enough comments', callback);
				testing.assertEquals(comments[0].text, text, 'Invalid comment text', callback);
				var date = new Date(comments[0].date);
				var elapsed = date.getTime() - then;
				testing.assert(elapsed < 10000, 'Too much time since last comment', callback);
				testing.success(callback);
			});
		});
	});

}

/**
 * Run the tests.
 */
exports.test = function(callback)
{
	testing.run([
		testCreateMoveCard,
		testReadList,
		testFindCard,
		testAddReadComment,
	], 50000, callback);
};

// start server if invoked directly
if (__filename == process.argv[1])
{
	log = new Log('debug');
	exports.test(testing.show);
}

<|endoftext|>


<commit_before>var request = require('request');
var db = require('./database');
var slack = require('./slack');
var _ = require('underscore');
var config = require('./config');
var choices = require('./choices');

module.exports = function (grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      bower: {
        options: {
          compress: true
        },
        files: {
          'public/js/vendor/compiled.min.js': 'public/js/vendor/compiled.js'
        }
      }
    },
    bower_concat: {
      all: {
        dest: 'public/js/vendor/compiled.js',
        cssDest: 'public/css/vendor/compiled.css',
      }
    }
  });
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-bower-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.registerTask('default', ['bower_concat', 'uglify:bower']);
  grunt.registerTask('increment-round', 'Moves to the next round of votes', function () {
    var done = this.async();
    db.incrementCurrentRound(done);
  });
  grunt.registerTask('chooser', 'Chooses the lunch destination for this week', function () {
    var done = this.async();

    db.getVotes(function (votes) {
      if (votes.length === 0) {
        grunt.log.fail("No votes!");
        done();
        return;
      } else {
        var choiceid;
        var choiceMessage;
        // check largest vote for large majority
        var majority = _.chain(votes).countBy(function (v) { return v.vote; }).pairs().max(function (arr) { return arr[1]; }).value();
        // More than 75% majority
        if (majority[1] > (votes.length * 0.75)) {
          choiceid = majority[0];
          choiceMessage = "by >75% majority";
          grunt.log.writeln(majority);
        } else {
          // Otherwise pick randomly
          var index = Math.floor(Math.random() * votes.length);

          choiceid = votes[index].vote;
          choiceMessage = "randomly";
        }
        var choice = _.find(choices.list, function (c) { return c.id == choiceid });

        slack.broadcast("The lunch destination has been chosen " + choiceMessage + ": " + choice.name, function (err) {
          if (err) {
            grunt.log.fail(err);
            done();
          } else {
            db.setWinner(choiceid, function (err) {
              grunt.log.write("The lunch destination has been chosen " + choiceMessage + ": " + choice.name);
              if (err) {
                grunt.log.warn("Couldn't set the winner: " + err);
              }
              done();
            });
          }
        });
      }
    });
  });

  grunt.registerTask('showvotes', 'Shows this week\'s votes', function () {
    var done = this.async();

    db.getVotes(function (votes) {
      if (votes.length === 0) {
        grunt.log.ok("No votes!");
        done();
        return;
      } else {
        grunt.log.ok(votes.length + " votes cast");
        for (var index = 0; index < votes.length; index++) {
          var vote = votes[index];
          var choice = _.find(choices.list, function (c) { return c.id == vote.vote });
          grunt.log.ok(vote.user + " voted for " + choice.name);
        }
        done();
      }
    });
  });

  grunt.registerTask('reminder', 'Sends the lunch reminder to Slack', function () {
    var done = this.async();
    slack.broadcast("Reminder: Vote for your choice for this week's lunch at http://lunch.jamiepenney.co.nz", function (err) {
      if (err) {
        grunt.log.fail(err);
      } else {
        grunt.log.write('Reminder sent');
      }
      done();
    });
  });

  grunt.registerTask('sendToken', 'Sends token and instrctions to a user', function() {
    var done = this.async();
    var user = grunt.option('user');

    if (!user) {
      grunt.log.error('specify a --user');
      return;
    }

    var configUser = _.find(config.users, function(u) { return u.user == user; });
    if (!configUser) {
      grunt.log.error('Couldn\'t find that user');
      return;
    }
    slack.directMessage('Here\s your http://lunch.jamiepenney.co.nz token: `' + configUser.token + '`', configUser.slackUsername, done);
  });
};<commit_msg>Remove debugging log call<commit_after>var request = require('request');
var db = require('./database');
var slack = require('./slack');
var _ = require('underscore');
var config = require('./config');
var choices = require('./choices');

module.exports = function (grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      bower: {
        options: {
          compress: true
        },
        files: {
          'public/js/vendor/compiled.min.js': 'public/js/vendor/compiled.js'
        }
      }
    },
    bower_concat: {
      all: {
        dest: 'public/js/vendor/compiled.js',
        cssDest: 'public/css/vendor/compiled.css',
      }
    }
  });
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-bower-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.registerTask('default', ['bower_concat', 'uglify:bower']);
  grunt.registerTask('increment-round', 'Moves to the next round of votes', function () {
    var done = this.async();
    db.incrementCurrentRound(done);
  });
  grunt.registerTask('chooser', 'Chooses the lunch destination for this week', function () {
    var done = this.async();

    db.getVotes(function (votes) {
      if (votes.length === 0) {
        grunt.log.fail("No votes!");
        done();
        return;
      } else {
        var choiceid;
        var choiceMessage;
        // check largest vote for large majority
        var majority = _.chain(votes).countBy(function (v) { return v.vote; }).pairs().max(function (arr) { return arr[1]; }).value();
        // More than 75% majority
        if (majority[1] > (votes.length * 0.75)) {
          choiceid = majority[0];
          choiceMessage = "by >75% majority";
        } else {
          // Otherwise pick randomly
          var index = Math.floor(Math.random() * votes.length);

          choiceid = votes[index].vote;
          choiceMessage = "randomly";
        }
        var choice = _.find(choices.list, function (c) { return c.id == choiceid });

        slack.broadcast("The lunch destination has been chosen " + choiceMessage + ": " + choice.name, function (err) {
          if (err) {
            grunt.log.fail(err);
            done();
          } else {
            db.setWinner(choiceid, function (err) {
              grunt.log.write("The lunch destination has been chosen " + choiceMessage + ": " + choice.name);
              if (err) {
                grunt.log.warn("Couldn't set the winner: " + err);
              }
              done();
            });
          }
        });
      }
    });
  });

  grunt.registerTask('showvotes', 'Shows this week\'s votes', function () {
    var done = this.async();

    db.getVotes(function (votes) {
      if (votes.length === 0) {
        grunt.log.ok("No votes!");
        done();
        return;
      } else {
        grunt.log.ok(votes.length + " votes cast");
        for (var index = 0; index < votes.length; index++) {
          var vote = votes[index];
          var choice = _.find(choices.list, function (c) { return c.id == vote.vote });
          grunt.log.ok(vote.user + " voted for " + choice.name);
        }
        done();
      }
    });
  });

  grunt.registerTask('reminder', 'Sends the lunch reminder to Slack', function () {
    var done = this.async();
    slack.broadcast("Reminder: Vote for your choice for this week's lunch at http://lunch.jamiepenney.co.nz", function (err) {
      if (err) {
        grunt.log.fail(err);
      } else {
        grunt.log.write('Reminder sent');
      }
      done();
    });
  });

  grunt.registerTask('sendToken', 'Sends token and instrctions to a user', function() {
    var done = this.async();
    var user = grunt.option('user');

    if (!user) {
      grunt.log.error('specify a --user');
      return;
    }

    var configUser = _.find(config.users, function(u) { return u.user == user; });
    if (!configUser) {
      grunt.log.error('Couldn\'t find that user');
      return;
    }
    slack.directMessage('Here\s your http://lunch.jamiepenney.co.nz token: `' + configUser.token + '`', configUser.slackUsername, done);
  });
};<|endoftext|>