<commit_before>"use strict";
/**
 * `directory` type prompt
 */
var rx = require("rx-lite");
var util = require("util");
var chalk = require("chalk");
var figures = require("figures");
var cliCursor = require("cli-cursor");
var Base = require("inquirer/lib/prompts/base");
var observe = require("inquirer/lib/utils/events");
var Paginator = require("inquirer/lib/utils/paginator");
var Choices = require("inquirer/lib/objects/choices");
var Separator = require("inquirer/lib/objects/separator");

var path = require("path");
var fs = require("fs");

/**
 * Constants
 */
var CHOOSE = 'choose this directory';
var BACK = '..';
var CURRENT = '.';

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */
function listRender(choices, pointer) {
    var output = '';
    var separatorOffset = 0;

    choices.forEach(function(choice, index) {
        if (choice.type === 'separator') {
            separatorOffset++;
            output += "  " + choice + "\n";
            return;
        }

        var isSelected = (index - separatorOffset === pointer);
        var line = (isSelected ? figures.pointer + ' ' : '  ') + choice.name;
        if (isSelected) {
            line = chalk.cyan(line);
        }
        output += line + ' \n';
    });

    return output.replace(/\n$/, '');
}

/**
 * Function for getting list of folders in directory
 * @param  {String} basePath the path the folder to get a list of containing folders
 * @return {Array}           array of folder names inside of basePath
 */
function getDirectories(basePath) {
    return fs
        .readdirSync(basePath)
        .filter(function(file) {
           try {
                var stats = fs.lstatSync(path.join(basePath, file));
                if (stats.isSymbolicLink()) {
                    return false;
                }
                var isDir = stats.isDirectory();
                var isNotDotFile = path.basename(file).indexOf('.') !== 0;
                return isDir && isNotDotFile;
            } catch (error) {
                return false;
            }
        })
        .sort();
}

/**
 * Constructor
 */
function Prompt() {
    Base.apply(this, arguments);
    if (!this.opt.basePath) {
        this.throwParamError('basePath');
    }
    this.currentPath = path.isAbsolute(this.opt.basePath) ? path.resolve(this.opt.basePath) : path.resolve(process.cwd(), this.opt.basePath);
    this.root = path.parse(this.currentPath).root;
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;

    // Make sure no default is set (so it won"t be printed)
    this.opt.default = null;

    this.searchTerm = '';

    this.paginator = new Paginator();
}
util.inherits(Prompt, Base);

/**
 * Start the Inquiry session
 * @param  {Function} callback      Callback when prompt is done
 * @return {this}
 */

Prompt.prototype._run = function(callback) {
    var self = this;
    self.searchMode = false;
    this.done = callback;
    var alphaNumericRegex = /\w|\.|\-/i;
    var events = observe(this.rl);

    var keyUps = events.keypress.filter(function(evt) {
        return evt.key.name === 'up' || (!self.searchMode && evt.key.name === 'k');
    }).share();

    var keyDowns = events.keypress.filter(function(evt) {
        return evt.key.name === 'down' || (!self.searchMode && evt.key.name === 'j');
    }).share();

    var keySlash = events.keypress.filter(function(evt) {
        return evt.value === '/';
    }).share();

    var keyMinus = events.keypress.filter(function(evt) {
        return evt.value === '-';
    }).share();

    var alphaNumeric = events.keypress.filter(function(evt) {
        return evt.key.name === 'backspace' || alphaNumericRegex.test(evt.value);
    }).share();

    var searchTerm = keySlash.flatMap(function() {
        self.searchMode = true;
        self.searchTerm = '';
        self.render();
        var end$ = new rx.Subject();
        var done$ = rx.Observable.merge(events.line, end$);
        return alphaNumeric.map(function(evt) {
                if (evt.key.name === 'backspace' && self.searchTerm.length) {
                    self.searchTerm = self.searchTerm.slice(0, -1);
                } else if (evt.value) {
                    self.searchTerm += evt.value;
                }
                if (self.searchTerm === '') {
                    end$.onNext(true);
                }
                return self.searchTerm;
            })
            .takeUntil(done$)
            .doOnCompleted(function() {
                self.searchMode = false;
                self.render();
                return false;
            });
    }).share();

    var outcome = this.handleSubmit(events.line);
    outcome.drill.forEach(this.handleDrill.bind(this));
    outcome.back.forEach(this.handleBack.bind(this));
    keyUps.takeUntil(outcome.done).forEach(this.onUpKey.bind(this));
    keyDowns.takeUntil(outcome.done).forEach(this.onDownKey.bind(this));
    keyMinus.takeUntil(outcome.done).forEach(this.handleBack.bind(this));
    events.keypress.takeUntil(outcome.done).forEach(this.hideKeyPress.bind(this));
    searchTerm.takeUntil(outcome.done).forEach(this.onKeyPress.bind(this));
    outcome.done.forEach(this.onSubmit.bind(this));

    // Init the prompt
    cliCursor.hide();
    this.render();

    return this;
};

/**
 * Render the prompt to screen
 * @return {Prompt} self
 */

Prompt.prototype.render = function() {
    // Render question
    var message = this.getQuestion();

    // Render choices or answer depending on the state
    if (this.status === 'answered') {
        message += chalk.cyan(this.currentPath);
    } else {
        message += chalk.bold('\n Current directory: ') + chalk.cyan(path.resolve(this.opt.basePath, this.currentPath));
        message += chalk.bold('\n');
        var choicesStr = listRender(this.opt.choices, this.selected);
        message += '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
        if (this.searchMode) {
            message += ('\nSearch: ' + this.searchTerm);
        } else {
            message += chalk.dim('\n(Use "/" key to search this directory)');
            message += chalk.dim('\n(Use "-" key to navigate to the parent folder');
        }
        message += chalk.dim('\n(Use arrow keys)');
    }
    this.screen.render(message);
};

/**
 * When user press `enter` key
 *
 * @param {any} e
 * @returns
 */
Prompt.prototype.handleSubmit = function(e) {
    var self = this;
    var obx = e.map(function() {
        return self.opt.choices.getChoice(self.selected).value;
    }).share();

    var done = obx.filter(function(choice) {
        return choice === CHOOSE || choice === CURRENT;
    }).take(1);
    var back = obx.filter(function(choice) {
        return choice === BACK;
    }).takeUntil(done);

    var drill = obx.filter(function(choice) {
        return choice !== BACK && choice !== CHOOSE && choice !== CURRENT;
    }).takeUntil(done);

    return {
        done: done,
        back: back,
        drill: drill
    };
};

/**
 *  when user selects to drill into a folder (by selecting folder name)
 */
Prompt.prototype.handleDrill = function() {
    var choice = this.opt.choices.getChoice(this.selected);
    this.currentPath = path.join(this.currentPath, choice.value);
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;
    this.render();
};

/**
 * when user selects ".. back"
 */
Prompt.prototype.handleBack = function() {
    this.currentPath = path.dirname(this.currentPath);
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;
    this.render();
};

/**
 * when user selects 'choose this folder'
 */
Prompt.prototype.onSubmit = function(/*value*/) {
    this.status = 'answered';

    // Rerender prompt
    this.render();

    this.screen.done();
    cliCursor.show();
    this.done(path.resolve(this.opt.basePath, this.currentPath));
};

/**
 * When user press a key
 */
Prompt.prototype.hideKeyPress = function() {
    if (!this.searchMode) {
        this.render();
    }
};

Prompt.prototype.onUpKey = function() {
    var len = this.opt.choices.realLength;
    this.selected = (this.selected > 0) ? this.selected - 1 : len - 1;
    this.render();
};

Prompt.prototype.onDownKey = function() {
    var len = this.opt.choices.realLength;
    this.selected = (this.selected < len - 1) ? this.selected + 1 : 0;
    this.render();
};

Prompt.prototype.onSlashKey = function(/*e*/) {
    this.render();
};

Prompt.prototype.onKeyPress = function(/*e*/) {
    var item;
    for (var index = 0; index < this.opt.choices.realLength; index++) {
        item = this.opt.choices.realChoices[index].name.toLowerCase();
        if (item.indexOf(this.searchTerm) === 0) {
            this.selected = index;
            break;
        }
    }
    this.render();
};

/**
 * Helper to create new choices based on previous selection.
 */
Prompt.prototype.createChoices = function(basePath) {
    var choices = getDirectories(basePath);
    if (basePath !== this.root) {
      choices.unshift(BACK);
    }
    choices.unshift(CURRENT);
    if (choices.length > 0) {
        choices.push(new Separator());
    }
    choices.push(CHOOSE);
    choices.push(new Separator());
    return choices;
};

/**
 * Module exports
 */

module.exports = Prompt;
<commit_msg>Remove duplicate message<commit_after>"use strict";
/**
 * `directory` type prompt
 */
var rx = require("rx-lite");
var util = require("util");
var chalk = require("chalk");
var figures = require("figures");
var cliCursor = require("cli-cursor");
var Base = require("inquirer/lib/prompts/base");
var observe = require("inquirer/lib/utils/events");
var Paginator = require("inquirer/lib/utils/paginator");
var Choices = require("inquirer/lib/objects/choices");
var Separator = require("inquirer/lib/objects/separator");

var path = require("path");
var fs = require("fs");

/**
 * Constants
 */
var CHOOSE = 'choose this directory';
var BACK = '..';
var CURRENT = '.';

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */
function listRender(choices, pointer) {
    var output = '';
    var separatorOffset = 0;

    choices.forEach(function(choice, index) {
        if (choice.type === 'separator') {
            separatorOffset++;
            output += "  " + choice + "\n";
            return;
        }

        var isSelected = (index - separatorOffset === pointer);
        var line = (isSelected ? figures.pointer + ' ' : '  ') + choice.name;
        if (isSelected) {
            line = chalk.cyan(line);
        }
        output += line + ' \n';
    });

    return output.replace(/\n$/, '');
}

/**
 * Function for getting list of folders in directory
 * @param  {String} basePath the path the folder to get a list of containing folders
 * @return {Array}           array of folder names inside of basePath
 */
function getDirectories(basePath) {
    return fs
        .readdirSync(basePath)
        .filter(function(file) {
           try {
                var stats = fs.lstatSync(path.join(basePath, file));
                if (stats.isSymbolicLink()) {
                    return false;
                }
                var isDir = stats.isDirectory();
                var isNotDotFile = path.basename(file).indexOf('.') !== 0;
                return isDir && isNotDotFile;
            } catch (error) {
                return false;
            }
        })
        .sort();
}

/**
 * Constructor
 */
function Prompt() {
    Base.apply(this, arguments);
    if (!this.opt.basePath) {
        this.throwParamError('basePath');
    }
    this.currentPath = path.isAbsolute(this.opt.basePath) ? path.resolve(this.opt.basePath) : path.resolve(process.cwd(), this.opt.basePath);
    this.root = path.parse(this.currentPath).root;
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;

    // Make sure no default is set (so it won"t be printed)
    this.opt.default = null;

    this.searchTerm = '';

    this.paginator = new Paginator();
}
util.inherits(Prompt, Base);

/**
 * Start the Inquiry session
 * @param  {Function} callback      Callback when prompt is done
 * @return {this}
 */

Prompt.prototype._run = function(callback) {
    var self = this;
    self.searchMode = false;
    this.done = callback;
    var alphaNumericRegex = /\w|\.|\-/i;
    var events = observe(this.rl);

    var keyUps = events.keypress.filter(function(evt) {
        return evt.key.name === 'up' || (!self.searchMode && evt.key.name === 'k');
    }).share();

    var keyDowns = events.keypress.filter(function(evt) {
        return evt.key.name === 'down' || (!self.searchMode && evt.key.name === 'j');
    }).share();

    var keySlash = events.keypress.filter(function(evt) {
        return evt.value === '/';
    }).share();

    var keyMinus = events.keypress.filter(function(evt) {
        return evt.value === '-';
    }).share();

    var alphaNumeric = events.keypress.filter(function(evt) {
        return evt.key.name === 'backspace' || alphaNumericRegex.test(evt.value);
    }).share();

    var searchTerm = keySlash.flatMap(function() {
        self.searchMode = true;
        self.searchTerm = '';
        self.render();
        var end$ = new rx.Subject();
        var done$ = rx.Observable.merge(events.line, end$);
        return alphaNumeric.map(function(evt) {
                if (evt.key.name === 'backspace' && self.searchTerm.length) {
                    self.searchTerm = self.searchTerm.slice(0, -1);
                } else if (evt.value) {
                    self.searchTerm += evt.value;
                }
                if (self.searchTerm === '') {
                    end$.onNext(true);
                }
                return self.searchTerm;
            })
            .takeUntil(done$)
            .doOnCompleted(function() {
                self.searchMode = false;
                self.render();
                return false;
            });
    }).share();

    var outcome = this.handleSubmit(events.line);
    outcome.drill.forEach(this.handleDrill.bind(this));
    outcome.back.forEach(this.handleBack.bind(this));
    keyUps.takeUntil(outcome.done).forEach(this.onUpKey.bind(this));
    keyDowns.takeUntil(outcome.done).forEach(this.onDownKey.bind(this));
    keyMinus.takeUntil(outcome.done).forEach(this.handleBack.bind(this));
    events.keypress.takeUntil(outcome.done).forEach(this.hideKeyPress.bind(this));
    searchTerm.takeUntil(outcome.done).forEach(this.onKeyPress.bind(this));
    outcome.done.forEach(this.onSubmit.bind(this));

    // Init the prompt
    cliCursor.hide();
    this.render();

    return this;
};

/**
 * Render the prompt to screen
 * @return {Prompt} self
 */

Prompt.prototype.render = function() {
    // Render question
    var message = this.getQuestion();

    // Render choices or answer depending on the state
    if (this.status === 'answered') {
        message += chalk.cyan(this.currentPath);
    } else {
        message += chalk.bold('\n Current directory: ') + chalk.cyan(path.resolve(this.opt.basePath, this.currentPath));
        message += chalk.bold('\n');
        var choicesStr = listRender(this.opt.choices, this.selected);
        message += '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
        if (this.searchMode) {
            message += ('\nSearch: ' + this.searchTerm);
        } else {
            message += chalk.dim('\n(Use "/" key to search this directory)');
            message += chalk.dim('\n(Use "-" key to navigate to the parent folder');
        }
    }
    this.screen.render(message);
};

/**
 * When user press `enter` key
 *
 * @param {any} e
 * @returns
 */
Prompt.prototype.handleSubmit = function(e) {
    var self = this;
    var obx = e.map(function() {
        return self.opt.choices.getChoice(self.selected).value;
    }).share();

    var done = obx.filter(function(choice) {
        return choice === CHOOSE || choice === CURRENT;
    }).take(1);
    var back = obx.filter(function(choice) {
        return choice === BACK;
    }).takeUntil(done);

    var drill = obx.filter(function(choice) {
        return choice !== BACK && choice !== CHOOSE && choice !== CURRENT;
    }).takeUntil(done);

    return {
        done: done,
        back: back,
        drill: drill
    };
};

/**
 *  when user selects to drill into a folder (by selecting folder name)
 */
Prompt.prototype.handleDrill = function() {
    var choice = this.opt.choices.getChoice(this.selected);
    this.currentPath = path.join(this.currentPath, choice.value);
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;
    this.render();
};

/**
 * when user selects ".. back"
 */
Prompt.prototype.handleBack = function() {
    this.currentPath = path.dirname(this.currentPath);
    this.opt.choices = new Choices(this.createChoices(this.currentPath), this.answers);
    this.selected = 0;
    this.render();
};

/**
 * when user selects 'choose this folder'
 */
Prompt.prototype.onSubmit = function(/*value*/) {
    this.status = 'answered';

    // Rerender prompt
    this.render();

    this.screen.done();
    cliCursor.show();
    this.done(path.resolve(this.opt.basePath, this.currentPath));
};

/**
 * When user press a key
 */
Prompt.prototype.hideKeyPress = function() {
    if (!this.searchMode) {
        this.render();
    }
};

Prompt.prototype.onUpKey = function() {
    var len = this.opt.choices.realLength;
    this.selected = (this.selected > 0) ? this.selected - 1 : len - 1;
    this.render();
};

Prompt.prototype.onDownKey = function() {
    var len = this.opt.choices.realLength;
    this.selected = (this.selected < len - 1) ? this.selected + 1 : 0;
    this.render();
};

Prompt.prototype.onSlashKey = function(/*e*/) {
    this.render();
};

Prompt.prototype.onKeyPress = function(/*e*/) {
    var item;
    for (var index = 0; index < this.opt.choices.realLength; index++) {
        item = this.opt.choices.realChoices[index].name.toLowerCase();
        if (item.indexOf(this.searchTerm) === 0) {
            this.selected = index;
            break;
        }
    }
    this.render();
};

/**
 * Helper to create new choices based on previous selection.
 */
Prompt.prototype.createChoices = function(basePath) {
    var choices = getDirectories(basePath);
    if (basePath !== this.root) {
      choices.unshift(BACK);
    }
    choices.unshift(CURRENT);
    if (choices.length > 0) {
        choices.push(new Separator());
    }
    choices.push(CHOOSE);
    choices.push(new Separator());
    return choices;
};

/**
 * Module exports
 */

module.exports = Prompt;
<|endoftext|>


<commit_before>import path from 'path';
import url from 'url';
import serveStatic from 'serve-static';
import * as utils from './utils';

// Find node_modules root path
let modulesRootPath = require.resolve('swagger-ui');
modulesRootPath = modulesRootPath.substr(0, modulesRootPath.lastIndexOf('node_modules'));

export default function init (config) {
  return function () {
    const app = this;

    // Apply configuration
    const rootDoc = Object.assign({
      paths: {},
      definitions: {},
      swagger: '2.0',
      schemes: ['http'],
      tags: [],
      basePath: '',
      docsPath: '/docs',
      consumes: ['application/json'],
      produces: ['application/json'],
      info: {},
      ignore: {
        tags: []
      }
    }, config || {});

    const docsPath = rootDoc.docsPath;

    // Create API for Documentation
    app.get(docsPath, function (req, res) {
      res.format({
        'application/json': function () {
          res.json(rootDoc);
        },

        'text/html': function () {
          const parsed = url.parse(req.url);
          const pathname = parsed.pathname;

          if (pathname[pathname.length - 1] !== '/') {
            parsed.pathname = `${req.baseUrl}${pathname}/`;
            return res.redirect(301, url.format(parsed));
          }

          if (typeof config.uiIndex === 'function') {
            config.uiIndex(req, res);
          } else if (typeof config.uiIndex === 'string') {
            res.sendFile(config.uiIndex);
          } else if (config.uiIndex === true) {
            if (req.query.url) {
              res.sendFile(path.join(modulesRootPath, 'node_modules/swagger-ui/dist/index.html'));
            } else {
              // Set swagger url (needed for default UI)
              res.redirect('?url=' + encodeURI(config.docsPath));
            }
          } else {
            res.json(rootDoc);
          }
        }
      });
    });

    if (typeof config.uiIndex !== 'undefined') {
      const uiPath = path.dirname(require.resolve('swagger-ui'));
      app.use(docsPath, serveStatic(uiPath));
    }

    app.docs = rootDoc;

    // Optional: Register this plugin as a Feathers provider
    app.providers.push(function (path, service) {
      service.docs = service.docs || {};

      // Load documentation from service, if available.
      const doc = service.docs;
      let version = config.versionPrefix ? path.match(config.versionPrefix) : null;
      version = version ? ' ' + version[0] : '';
      const apiPath = path.replace(config.prefix, '');
      const group = apiPath.split('/');
      const tag = (apiPath.indexOf('/') > -1 ? group[0] : apiPath) + version;
      const model = apiPath.indexOf('/') > -1 ? group[1] : apiPath;
      const security = {};

      if (rootDoc.security) {
        security[rootDoc.security.name] = [];
      }

      if (rootDoc.ignore.tags.indexOf(tag) > -1) {
        return;
      }

      const pathObj = rootDoc.paths;
      const withIdKey = `/${path}/{${service.id || 'id'}}`;
      const withoutIdKey = `/${path}`;
      const securities = doc.securities || [];

      if (typeof doc.definition !== 'undefined') {
        rootDoc.definitions[tag] = doc.definition;
      }

      if (typeof doc.definitions !== 'undefined') {
        rootDoc.definitions = Object.assign(rootDoc.definitions, doc.definitions);
      }

      // FIND
      if (typeof service.find === 'function') {
        pathObj[withoutIdKey] = pathObj[withoutIdKey] || {};
        pathObj[withoutIdKey].get = utils.operation('find', service, {
          tags: [tag],
          description: 'Retrieves a list of all resources from the service.',
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('find') > -1 ? security : {}
        });
      }

      // GET
      if (typeof service.get === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].get = utils.operation('get', service, {
          tags: [tag],
          description: 'Retrieves a single resource with the given id from the service.',
          parameters: [{
            description: `ID of ${model} to return`,
            in: 'path',
            required: true,
            name: 'resourceId',
            type: 'integer'
          }],
          responses: {
            '200': {
              description: 'success'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('get') > -1 ? security : {}
        });
      }

      // CREATE
      if (typeof service.create === 'function') {
        pathObj[withoutIdKey] = pathObj[withoutIdKey] || {};
        pathObj[withoutIdKey].post = utils.operation('create', service, {
          tags: [tag],
          description: 'Creates a new resource with data.',
          parameters: [{
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + model}
          }],
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('create') > -1 ? security : {}
        });
      }

      // UPDATE
      if (typeof service.update === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].put = utils.operation('update', service, {
          tags: [tag],
          description: 'Updates the resource identified by id using data.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: 'resourceId',
            type: 'integer'
          }, {
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + model}
          }],
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('update') > -1 ? security : {}
        });
      }

      // PATCH
      if (typeof service.patch === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].patch = utils.operation('patch', service, {
          tags: [tag],
          description: 'Updates the resource identified by id using data.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: 'resourceId',
            type: 'integer'
          }, {
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + model}
          }],
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('patch') > -1 ? security : {}
        });
      }

      // REMOVE
      if (typeof service.remove === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].delete = utils.operation('remove', service, {
          tags: [tag],
          description: 'Removes the resource with id.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: 'resourceId',
            type: 'integer'
          }],
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('remove') > -1 ? security : {}
        });
      }

      rootDoc.paths = pathObj;

      if (!rootDoc.tags.find(item => item.name === tag)) {
        rootDoc.tags.push(utils.tag(tag, doc));
      }
    });
  };
}

Object.assign(init, utils);
<commit_msg>Quality of life (#68)<commit_after>import path from 'path';
import url from 'url';
import serveStatic from 'serve-static';
import * as utils from './utils';

// Find node_modules root path
let modulesRootPath = require.resolve('swagger-ui');
modulesRootPath = modulesRootPath.substr(0, modulesRootPath.lastIndexOf('node_modules'));

export default function init (config) {
  return function () {
    const app = this;

    // Apply configuration
    const rootDoc = Object.assign({
      paths: {},
      definitions: {},
      swagger: '2.0',
      schemes: ['http'],
      tags: [],
      basePath: '',
      docsPath: '/docs',
      consumes: ['application/json'],
      produces: ['application/json'],
      info: {},
      ignore: {
        tags: []
      }
    }, config || {});

    const docsPath = rootDoc.docsPath;

    // Create API for Documentation
    app.get(docsPath, function (req, res) {
      res.format({
        'application/json': function () {
          res.json(rootDoc);
        },

        'text/html': function () {
          const parsed = url.parse(req.url);
          const pathname = parsed.pathname;

          if (pathname[pathname.length - 1] !== '/') {
            parsed.pathname = `${req.baseUrl}${pathname}/`;
            return res.redirect(301, url.format(parsed));
          }

          if (typeof config.uiIndex === 'function') {
            config.uiIndex(req, res);
          } else if (typeof config.uiIndex === 'string') {
            res.sendFile(config.uiIndex);
          } else if (config.uiIndex === true) {
            if (req.query.url) {
              res.sendFile(path.join(modulesRootPath, 'node_modules/swagger-ui/dist/index.html'));
            } else {
              // Set swagger url (needed for default UI)
              res.redirect('?url=' + encodeURI(config.docsPath));
            }
          } else {
            res.json(rootDoc);
          }
        }
      });
    });

    if (typeof config.uiIndex !== 'undefined') {
      const uiPath = path.dirname(require.resolve('swagger-ui'));
      app.use(docsPath, serveStatic(uiPath));
    }

    app.docs = rootDoc;

    // Optional: Register this plugin as a Feathers provider
    app.providers.push(function (path, service) {
      service.docs = service.docs || {};

      // Load documentation from service, if available.
      const doc = service.docs;
      const idName = service.id || 'id';
      const idType = doc.idType || 'integer';
      let version = config.versionPrefix ? path.match(config.versionPrefix) : null;
      version = version ? ' ' + version[0] : '';
      const apiPath = path.replace(config.prefix, '');
      const group = apiPath.split('/');
      const tag = (apiPath.indexOf('/') > -1 ? group[0] : apiPath) + version;
      const model = apiPath.indexOf('/') > -1 ? group[1] : apiPath;
      const security = {};

      if (rootDoc.security) {
        security[rootDoc.security.name] = [];
      }

      if (rootDoc.ignore.tags.indexOf(tag) > -1) {
        return;
      }

      const pathObj = rootDoc.paths;
      const withIdKey = `/${path}/{${idName}}`;
      const withoutIdKey = `/${path}`;
      const securities = doc.securities || [];

      if (typeof doc.definition !== 'undefined') {
        rootDoc.definitions[tag] = doc.definition;
        rootDoc.definitions[`${tag} list`] = {
          type: 'array',
          items: doc.definition
        };
      }

      if (typeof doc.definitions !== 'undefined') {
        rootDoc.definitions = Object.assign(rootDoc.definitions, doc.definitions);
      }

      // FIND
      if (typeof service.find === 'function') {
        pathObj[withoutIdKey] = pathObj[withoutIdKey] || {};
        pathObj[withoutIdKey].get = utils.operation('find', service, {
          tags: [tag],
          description: 'Retrieves a list of all resources from the service.',
          parameters: [
            {
              description: 'Number of results to return',
              in: 'query',
              name: '$limit',
              type: 'integer'
            },
            {
              description: 'Number of results to skip',
              in: 'query',
              name: '$skip',
              type: 'integer'
            },
            {
              description: 'Property to sort results',
              in: 'query',
              name: '$sort',
              type: 'string'
            }
          ],
          responses: {
            '200': {
              description: 'success',
              schema: {'$ref': '#/definitions/' + `${tag} list`}
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('find') > -1 ? security : {}
        });
      }

      // GET
      if (typeof service.get === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].get = utils.operation('get', service, {
          tags: [tag],
          description: 'Retrieves a single resource with the given id from the service.',
          parameters: [{
            description: `ID of ${model} to return`,
            in: 'path',
            required: true,
            name: idName,
            type: idType
          }],
          responses: {
            '200': {
              description: 'success',
              schema: {'$ref': '#/definitions/' + tag}
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            },
            '404': {
              description: 'not found'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('get') > -1 ? security : {}
        });
      }

      // CREATE
      if (typeof service.create === 'function') {
        pathObj[withoutIdKey] = pathObj[withoutIdKey] || {};
        pathObj[withoutIdKey].post = utils.operation('create', service, {
          tags: [tag],
          description: 'Creates a new resource with data.',
          parameters: [{
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + tag}
          }],
          responses: {
            '201': {
              description: 'created'
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('create') > -1 ? security : {}
        });
      }

      // UPDATE
      if (typeof service.update === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].put = utils.operation('update', service, {
          tags: [tag],
          description: 'Updates the resource identified by id using data.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: idName,
            type: idType
          }, {
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + tag}
          }],
          responses: {
            '200': {
              description: 'success',
              schema: {'$ref': '#/definitions/' + tag}
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            },
            '404': {
              description: 'not found'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('update') > -1 ? security : {}
        });
      }

      // PATCH
      if (typeof service.patch === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].patch = utils.operation('patch', service, {
          tags: [tag],
          description: 'Updates the resource identified by id using data.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: idName,
            type: idType
          }, {
            in: 'body',
            name: 'body',
            required: true,
            schema: {'$ref': '#/definitions/' + tag}
          }],
          responses: {
            '200': {
              description: 'success',
              schema: {'$ref': '#/definitions/' + tag}
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            },
            '404': {
              description: 'not found'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('patch') > -1 ? security : {}
        });
      }

      // REMOVE
      if (typeof service.remove === 'function') {
        pathObj[withIdKey] = pathObj[withIdKey] || {};
        pathObj[withIdKey].delete = utils.operation('remove', service, {
          tags: [tag],
          description: 'Removes the resource with id.',
          parameters: [{
            description: 'ID of ' + model + ' to return',
            in: 'path',
            required: true,
            name: idName,
            type: idType
          }],
          responses: {
            '200': {
              description: 'success',
              schema: {'$ref': '#/definitions/' + tag}
            },
            '500': {
              description: 'general error'
            },
            '401': {
              description: 'not authenticated'
            },
            '404': {
              description: 'not found'
            }
          },
          produces: rootDoc.produces,
          consumes: rootDoc.consumes,
          security: securities.indexOf('remove') > -1 ? security : {}
        });
      }

      rootDoc.paths = pathObj;

      if (!rootDoc.tags.find(item => item.name === tag)) {
        rootDoc.tags.push(utils.tag(tag, doc));
      }
    });
  };
}

Object.assign(init, utils);
<|endoftext|>


<commit_before>import { NativeModules, NativeEventEmitter, DeviceEventEmitter, Platform } from 'react-native'

const { RNBranch, RNBranchEventEmitter } = NativeModules
const nativeEventEmitter = Platform.select({
  android: DeviceEventEmitter,
  ios: new NativeEventEmitter(RNBranchEventEmitter)
})

import createBranchUniversalObject from './branchUniversalObject'

const INIT_SESSION_TTL = 5000

export const AddToWishlistEvent = "Add to Wishlist"
export const PurchasedEvent = "Purchased"
export const PurchaseInitiatedEvent = "Purchase Started"
export const RegisterViewEvent = "View"
export const ShareCompletedEvent = "Share Completed"
export const ShareInitiatedEvent = "Share Started"

class Branch {
  _launchTime = new Date().getTime();
  _debug = false;

  constructor(options = {}) {
    if (options.debug) this._debug = true
  }

  subscribe(listener) {
    /*
     * If this is within the INIT_SESSION_TTL, get the cached value from the native layer (asynchronously).
     * If none, the listener is not called. If there is a cached value, it is passed to the listener.
     */
    if (this._timeSinceLaunch() < INIT_SESSION_TTL) {
      RNBranch.redeemInitSessionResult().then((result) => {
        if (result) {
          listener(result)
        }
      })
    }

    const successSubscription = nativeEventEmitter.addListener(RNBranch.INIT_SESSION_SUCCESS, listener)
    const errorSubscription = nativeEventEmitter.addListener(RNBranch.INIT_SESSION_ERROR, listener)

    const unsubscribe = () => {
      successSubscription.remove()
      errorSubscription.remove()
    }

    return unsubscribe
  }

  _timeSinceLaunch() {
    return new Date().getTime() - this._launchTime
  }

  /*** RNBranch singleton methods ***/
  setDebug = RNBranch.setDebug
  getLatestReferringParams = RNBranch.getLatestReferringParams
  getFirstReferringParams = RNBranch.getFirstReferringParams
  setIdentity = (identity) => RNBranch.setIdentity(identity)
  logout = RNBranch.logout
  userCompletedAction = (event, state = {}) => RNBranch.userCompletedAction(event, state)
  getShortUrl = RNBranch.getShortUrl

  /*** Referral Methods ***/
  redeemRewards = (amount, bucket) => RNBranch.redeemRewards(amount, bucket)
  loadRewards = RNBranch.loadRewards
  getCreditHistory = RNBranch.getCreditHistory

  /*** BranchUniversalObject ***/
  createBranchUniversalObject = createBranchUniversalObject
}

export { Branch }
export default new Branch()
<commit_msg>Implement solution to #79 as verified in app testing.<commit_after>import { NativeModules, NativeEventEmitter, DeviceEventEmitter, Platform } from 'react-native'

const { RNBranch, RNBranchEventEmitter } = NativeModules
const nativeEventEmitter = Platform.select({
  android: DeviceEventEmitter,
  ios: new NativeEventEmitter(RNBranchEventEmitter)
})

import createBranchUniversalObject from './branchUniversalObject'

const INIT_SESSION_TTL = 5000

export const AddToWishlistEvent = "Add to Wishlist"
export const PurchasedEvent = "Purchased"
export const PurchaseInitiatedEvent = "Purchase Started"
export const RegisterViewEvent = "View"
export const ShareCompletedEvent = "Share Completed"
export const ShareInitiatedEvent = "Share Started"

class Branch {
  _launchTime = new Date().getTime();
  _debug = false;

  constructor(options = {}) {
    if (options.debug) this._debug = true
  }

  subscribe(listener) {
    /*
     * If this is within the INIT_SESSION_TTL, get the cached value from the native layer (asynchronously).
     * If none, the listener is not called. If there is a cached value, it is passed to the listener.
     */
    if (this._timeSinceLaunch() < INIT_SESSION_TTL) {
      RNBranch.redeemInitSessionResult().then((result) => {
        if (result) {
          listener(result)
        }

        /*
         * https://github.com/BranchMetrics/react-native-branch-deep-linking/issues/79
         *
         * By waiting until redeemInitSessionResult() returns, we roughly simulate a
         * synchronous call to the native layer.
         *
         * Note that this is equivalent to
         *
         * let result = await RNBranch.redeemInitSessionResult()
         * if (result) listener(result)
         * this._addListener(listener)
         *
         * But by using then(), the subscribe method does not have to be async.
         * This way, we don't add event listeners until the listener has received the
         * initial cached value, which essentially eliminates all possibility of
         * getting the same event twice.
         */
         this._addListener(listener)
      })
    }
    else {
      this._addListener(listener)
    }

    const unsubscribe = () => {
      this._removeListener(listener)
    }

    return unsubscribe
  }

  _timeSinceLaunch() {
    return new Date().getTime() - this._launchTime
  }

  _addListener(listener) {
    nativeEventEmitter.addListener(RNBranch.INIT_SESSION_SUCCESS, listener)
    nativeEventEmitter.addListener(RNBranch.INIT_SESSION_ERROR, listener)
  }

  _removeListener(listener) {
    nativeEventEmitter.removeListener(RNBranch.INIT_SESSION_SUCCESS, listener)
    nativeEventEmitter.removeListener(RNBranch.INIT_SESSION_ERROR, listener)
  }

  /*** RNBranch singleton methods ***/
  setDebug = RNBranch.setDebug
  getLatestReferringParams = RNBranch.getLatestReferringParams
  getFirstReferringParams = RNBranch.getFirstReferringParams
  setIdentity = (identity) => RNBranch.setIdentity(identity)
  logout = RNBranch.logout
  userCompletedAction = (event, state = {}) => RNBranch.userCompletedAction(event, state)
  getShortUrl = RNBranch.getShortUrl

  /*** Referral Methods ***/
  redeemRewards = (amount, bucket) => RNBranch.redeemRewards(amount, bucket)
  loadRewards = RNBranch.loadRewards
  getCreditHistory = RNBranch.getCreditHistory

  /*** BranchUniversalObject ***/
  createBranchUniversalObject = createBranchUniversalObject
}

export { Branch }
export default new Branch()
<|endoftext|>


<commit_before>import request from 'request-promise';
import config from '../config';
import { merge } from 'lodash';

var env = process.env;

function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

class ClashApi {
  constructor({uri, token, request} = {}) {
    this.token = token || env.COC_API_TOKEN;
    this.uri = uri || config.uri;
    this.requestDefaults = request || {};
    if (!this.token) {
      throw new Error('Must define a token option or COC_API_TOKEN env variable');
    }
  }

  requestOptions(opts) {
    return merge({
      headers: {
        Accept: 'application/json',
        authorization: `Bearer ${this.token}`
      },
      json: true
    }, opts, this.requestDefaults);
  }

  clanByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}`,
    }))
  }

  clanMembersByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/members`,
    }))
  }

  clanWarlogByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/warlog`,
    }))
  }

  clanCurrentWarByTag(tag) {
        return request(this.requestOptions({
            uri: `${this.uri}/clans/${encodeURIComponent(tag)}/currentwar`,
        }))
  }

  clanLeague(tag) {
        return request(this.requestOptions({
            uri: `${this.uri}/clans/${encodeURIComponent(tag)}/currentwar/leaguegroup`,
        }))
  }

    clans() {
    var qs = {};

    var dsl = [
      'name',
      'warFrequency',
      'locationId',
      'minMembers',
      'maxMembers',
      'minClanPoints',
      'minClanLevel',
      'limit',
      'after',
      'before'
    ].reduce((builder, field) => {
      builder[`with${capitalizeFirstLetter(field) }`] = (input) => {
        qs[field] = input;
        return builder;
      };
      return builder;
    }, {
      fetch: function() {
        return request(this.requestOptions({
          qs: qs,
          uri: `${this.uri}/clans`,
        }));
      }.bind(this)
    });

    return dsl;
  }

  locations() {
    var dsl = {
      fetch: function() {
        return request(this.requestOptions({
          uri: `${this.uri}/locations`,
        }));
      }.bind(this),
      withId: function (locId) {
        var rankId;

        var rankingDslMembers = {
          byClan: function () {
            rankId = 'clans';
            return rankingDsl;
          },
          byPlayer: function () {
            rankId = 'players';
            return rankingDsl;
          }
        };

        var rankingDsl = assign({
          fetch: function () {
            return request(this.requestOptions({
              uri: `${this.uri}/locations/${encodeURIComponent(locId)}/rankings/${rankId}`,
            }));
          }.bind(this)
        }, rankingDslMembers);

        var locDsl = assign({
          fetch: function () {
            return request(this.requestOptions({
              uri: `${this.uri}/locations/${encodeURIComponent(locId)}`,
            }));
          }
        }, rankingDslMembers);

        return locDsl;
      }.bind(this)
    }
    return dsl;
  }

  leagues() {
    return request(this.requestOptions({
      uri: `${this.uri}/leagues`,
    }));
  }

  playerByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/players/${encodeURIComponent(tag)}`,
    }))
  }
}

var factory = function (config) {
  return new ClashApi(config);
}

factory.ClashApi = ClashApi;

module.exports = factory;
<commit_msg>Documentation of methods<commit_after>import request from 'request-promise';
import config from '../config';
import { merge } from 'lodash';

const env = process.env;

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

/**
 * ClashApi - Provides an easy way to get started with the Clash of Clans API.
 *
 * All fetches return a promise using
 *
 * @example
 * let client = clashApi({
 *    token: yourApiToken // Optional, can also use COC_API_TOKEN env variable
 * });
 */
class ClashApi {

  constructor({uri, token, request} = {}) {
    /**
     * Personal Clash of Clans API token.
     */
    this.token = token || env.COC_API_TOKEN;
    this.uri = uri || config.uri;
    this._requestDefaults = request || {};
    if (!this.token) {
      throw new Error('Must define a token option or COC_API_TOKEN env variable');
    }
  }

  requestOptions(opts) {
    return merge({
      headers: {
        Accept: 'application/json',
        authorization: `Bearer ${this.token}`
      },
      json: true
    }, opts, this._requestDefaults);
  }

  /**
   * Get information about a single clan by clan tag. Clan tags can be found using clan search operation.
   *
   * @example
   * client
   *    .clanByTag('#UPC2UQ')
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err));
   *
   * @param {string} tag - Tag of the clan to retrieve.
   */
  clanByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}`,
    }))
  }

  /**
   * List clan members.
   *
   * @example
   * client
   *    .clanMembersByTag('#UPC2UQ')
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err));
   *
   * @param {string} tag - Tag of the clan whose members to retrieve.
   */
  clanMembersByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/members`,
    }))
  }

  /**
   * Retrieve clan's clan war log.
   *
   * @example
   * client
   *    .clanWarlogByTag('#UPC2UQ')
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err));
   *
   * @param {string} tag - Tag of the clan whose war log to retrieve.
   */
  clanWarlogByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/warlog`,
    }))
  }

  /**
   * Retrieve information about clan's current clan war.
   *
   * @example
   * client
   *    .clanCurrentWarByTag(`#UPC2UQ`)
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err));
   *
   * @param {string} tag - Tag of the clan whose current clan war information to retrieve.
   */
  clanCurrentWarByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/currentwar`,
    }))
  }

  /**
   * Retrieve information about clan's current clan war league group.
   *
   * @param {string} tag - Tag of the clan whose current clan war league group to retrieve.
   */
  clanLeague(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/clans/${encodeURIComponent(tag)}/currentwar/leaguegroup`,
    }))
  }

  /**
   * Search all clans by name and/or filtering the results using various criteria.
   * At least one filtering criteria must be defined and if name is used as part of search,
   * it is required to be at least three characters long.
   *
   * It is not possible to specify ordering for results so clients should not rely on any
   * specific ordering as that may change in the future releases of the API.
   *
   * @example
   * client
   *    .clans()
   *    .withWarFrequency('always')
   *    .withMinMembers(25)
   *    .fetch()
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err))
   *
   * @see https://developer.clashofclans.com/api-docs/index.html#!/clans/searchClans
   */
  clans() {
    const qs = {};

    const dsl = [
      'name',
      'warFrequency',
      'locationId',
      'minMembers',
      'maxMembers',
      'minClanPoints',
      'minClanLevel',
      'limit',
      'after',
      'before'
    ].reduce((builder, field) => {
      builder[`with${capitalizeFirstLetter(field) }`] = (input) => {
        qs[field] = input;
        return builder;
      };
      return builder;
    }, {
      fetch: function () {
        return request(this.requestOptions({
          qs: qs,
          uri: `${this.uri}/clans`,
        }));
      }.bind(this)
    });

    return dsl;
  }

  /**
   * List all available locations.
   *
   * @example
   * client
   *    .locations()
   *    .withId(locationId)
   *    .byPlayer()
   *    .fetch()
   *    .then(response => console.log(response))
   *    .catch(err => console.log(err));
   */
  locations() {
    const dsl = {
      fetch: function () {
        return request(this.requestOptions({
          uri: `${this.uri}/locations`,
        }));
      }.bind(this),
      withId: function (locId) {
        let rankId;

        const rankingDslMembers = {
          byClan: function () {
            rankId = 'clans';
            return rankingDsl;
          },
          byPlayer: function () {
            rankId = 'players';
            return rankingDsl;
          }
        };

        const rankingDsl = assign({
          fetch: function () {
            return request(this.requestOptions({
              uri: `${this.uri}/locations/${encodeURIComponent(locId)}/rankings/${rankId}`,
            }));
          }.bind(this)
        }, rankingDslMembers);

        const locDsl = assign({
          fetch: function () {
            return request(this.requestOptions({
              uri: `${this.uri}/locations/${encodeURIComponent(locId)}`,
            }));
          }
        }, rankingDslMembers);

        return locDsl;
      }.bind(this)
    }
    return dsl;
  }

  /**
   * Get list of leagues.
   */
  leagues() {
    return request(this.requestOptions({
      uri: `${this.uri}/leagues`,
    }));
  }

  /**
   * Get information about a single player by player tag. Player tags can be found either in game or by from clan member lists.
   */
  playerByTag(tag) {
    return request(this.requestOptions({
      uri: `${this.uri}/players/${encodeURIComponent(tag)}`,
    }))
  }
}

const factory = function (config) {
  return new ClashApi(config);
}

factory.ClashApi = ClashApi;

module.exports = factory;<|endoftext|>


<commit_before>import Image from './components/Image'

export { Image } // deprecated, will be dropped in v2.0.0
export default Image
<commit_msg>Removed deprecated export.<commit_after>import Image from './components/Image'

export default Image
<|endoftext|>


<commit_before>'use strict';

// Include the serverless-slack bot framework
const slack = require('serverless-slack');
const commands = require('./commands.js');

module.exports.handler = slack.handler.bind(slack);

const debug = (parts, msg, bot) => {
  bot.reply({
    text: 'parts:\n' +
      '```\n' +
      JSON.stringify(parts, null, 2) + '\n' +
      '```\n' +
      'msg:\n' +
      '```\n' +
      JSON.stringify(msg, null, 2) + '\n' +
      '```\n' +
      'bot:\n' +
      '```\n' +
      JSON.stringify(bot, null, 2) + '\n' +
      '```\n'
  });
};

const help = (parts, msg, bot) => {
  bot.reply({
    text: 'How can I be of service to you?\n' +
      ' * `standings|group|division` - provide group/division standings based on this channel name\n' +
      ' * `tier` - provide division standings based on this channel name\n' +
      'To run any of these commands, simply type `@lobot <command>`'
  });
};

const notImplmented = (bot) => {
  bot.reply({
    text: "I'm sorry, I don't know how to do that yet"
  });
};

// Slash Command handler
slack.on('message', (msg, bot) => {
  const botId = bot.auth.bot.bot_user_id;
  if (msg.event.text && !(msg.event.text.startsWith(`<@${botId}>`) || msg.event.text.startsWith('@lobot '))) {
    return;
  }

  console.log(`@lobot`, msg.event);

  let parts = msg.event.text.split(' ');
  if (parts.length === 1) {
    return;
  }

  console.log(`Processing message: ${msg.event.text} @ ${msg.event.event_ts}`);
  let cmd = parts[1];
  if (cmd === 'help') {
    help(parts, msg, bot);
  } else if (cmd === 'standings') {
    commands.respondWithStandings(bot, msg);
  } else if (cmd === 'division' || cmd === 'group') {
    commands.respondWithGroupStandings(bot, msg);
  } else if (cmd === 'tier') {
    commands.respondWithTierStandings(bot, msg);
  } else if (cmd === 'league') {
    notImplmented(bot);
  } else if (cmd === 'debug') {
    debug(parts, msg, bot);
  }
});
<commit_msg>Remove lobot log message<commit_after>'use strict';

// Include the serverless-slack bot framework
const slack = require('serverless-slack');
const commands = require('./commands.js');

module.exports.handler = slack.handler.bind(slack);

const debug = (parts, msg, bot) => {
  bot.reply({
    text: 'parts:\n' +
      '```\n' +
      JSON.stringify(parts, null, 2) + '\n' +
      '```\n' +
      'msg:\n' +
      '```\n' +
      JSON.stringify(msg, null, 2) + '\n' +
      '```\n' +
      'bot:\n' +
      '```\n' +
      JSON.stringify(bot, null, 2) + '\n' +
      '```\n'
  });
};

const help = (parts, msg, bot) => {
  bot.reply({
    text: 'How can I be of service to you?\n' +
      ' * `standings|group|division` - provide group/division standings based on this channel name\n' +
      ' * `tier` - provide division standings based on this channel name\n' +
      'To run any of these commands, simply type `@lobot <command>`'
  });
};

const notImplmented = (bot) => {
  bot.reply({
    text: "I'm sorry, I don't know how to do that yet"
  });
};

// Slash Command handler
slack.on('message', (msg, bot) => {
  const botId = bot.auth.bot.bot_user_id;
  if (msg.event.text && !(msg.event.text.startsWith(`<@${botId}>`) || msg.event.text.startsWith('@lobot '))) {
    return;
  }

  let parts = msg.event.text.split(' ');
  if (parts.length === 1) {
    return;
  }

  console.log(`Processing message: ${msg.event.text} @ ${msg.event.event_ts}`);
  let cmd = parts[1];
  if (cmd === 'help') {
    help(parts, msg, bot);
  } else if (cmd === 'standings') {
    commands.respondWithStandings(bot, msg);
  } else if (cmd === 'division' || cmd === 'group') {
    commands.respondWithGroupStandings(bot, msg);
  } else if (cmd === 'tier') {
    commands.respondWithTierStandings(bot, msg);
  } else if (cmd === 'league') {
    notImplmented(bot);
  } else if (cmd === 'debug') {
    debug(parts, msg, bot);
  }
});
<|endoftext|>


<commit_before>// cheap way of doing AA
//c.width = 3200, c.height = 1800; // 16:9 aspect ratio
c.width = 192, c.height = 108; // battery saving

f = new AudioContext;
a = f.createScriptProcessor(512, t = 1, K = 1);
a.connect(f.destination);

// sequencer thing
s=(notes,octave,rate,len) =>
  31 & t * Math.pow(2, notes[(t>>rate)%len] / 12 - octave)

// music debug
//t= 1744100
//t= 3744100
//t= 4744100

// music
X = a.onaudioprocess = e =>
  //for(L = e.outputBuffer.getChannelData(i=0);i++<512;t++) {
  //e.outputBuffer.getChannelData(i=0);i++<512;t++) {
  e.outputBuffer.getChannelData(i=0).map(_ =>
    e.outputBuffer.getChannelData(0)[i++] =
    (
      (
        // kick drum
        (
          (
            K = 1e4 / (
              t & 16382
              /* * (
                (t>>14) % 16 - 15 ? 1 : .75
              )*/
            )
          ) & 1
        ) * 30

        // bass
        + s('7050',4,17,4) / K
      )

      // turn off above instruments after a while
      * !(t>>22)

      // hihat TODO improve/golf envelope
      + (t % 100 * t & 128) * Math.min(.2, (1e1 / ((t>>3) % 512))) * !!(t>>19)

      // arpeggio
      + (s((t>>17)%2 ? '027' : '037',1,13-(3*(t>>20)%12),4)) / K * !!(t>>20)

    ) * (
      // fade out
      X = Math.min(1, Math.max(1e-9, 1e1-t++/5e5))
    ) / 200
  );

    //L[i] *= X / 200;
    //if (L[i] > 1) console.log('clipping');

    // limit volume while testing
    //L[i] = Math.max(-0.5, Math.min(0.5, L[i]));
  //}

// gfx
g = c.getContext`webgl`;
P = g.createProgram();

// NOTE: 2nd argument to drawArrays used to be 0, but undefined works
r = time => g.drawArrays(g.TRIANGLE_FAN,
  // Send resolution and time to shader
  g.uniform4f(g.getUniformLocation(P, 'a'), c.width, c.height, time / 1e3, X),
  3,
  g.uniform4f(g.getUniformLocation(P, 'b'), 1, 0, .2/K, requestAnimationFrame(r))
);

// vertex shader
g.shaderSource(S=g.createShader(g.VERTEX_SHADER), require("./vertex.glsl"));
g.compileShader(S);g.attachShader(P,S);

// fragment shader
g.shaderSource(S=g.createShader(g.FRAGMENT_SHADER), require("./fragment.glsl"));
g.compileShader(S);g.attachShader(P,S);

// Log compilation errors
// if (!g.getShaderParameter(S, 35713)) { // COMPILE_STATUS = 35713
//   throw g.getShaderInfoLog(S);
// }

g.bindBuffer(g.ARRAY_BUFFER, g.createBuffer(c.parentElement.style.margin = 0));
// 1st argument to enableVertexAttribArray used to be 0, but undefined works
// 1st argument to vertexAttribPointer used to be 0, but undefined works
g.vertexAttribPointer(
  g.enableVertexAttribArray(
    g.bufferData(g.ARRAY_BUFFER, Int8Array.of(-3, 1, 1, -3, 1, 1), g.STATIC_DRAW)
  ),
2, g.BYTE, r(c.style.height = '100vh'), g.linkProgram(P), g.useProgram(P));
<commit_msg>more comments<commit_after>// cheap way of doing AA
//c.width = 3200, c.height = 1800; // 16:9 aspect ratio
c.width = 192, c.height = 108; // battery saving

f = new AudioContext;
a = f.createScriptProcessor(512, t = 1, K = 1);
a.connect(f.destination);

// sequencer thing
s=(notes,octave,rate,len) =>
  31 & t * Math.pow(2, notes[(t>>rate) % len] / 12 - octave)

// music
X = a.onaudioprocess = e =>
  e.outputBuffer.getChannelData(i=0).map(_ =>
    e.outputBuffer.getChannelData(0)[i++] =
    (
      (
        // kick drum
        ((
          // envelope
          K = 1e4 / (
            t & 16382
            // nice pattern variation if it fits
            /* * (
              (t>>14) % 16 - 15 ? 1 : .75
            )*/
          )
        ) & 1) * 30

        // bass
        + s(
          '7050', // melody
          4,      // octave
          17,     // rate
          4       // melody length
        ) / K
      )

      // turn off above instruments after a while
      * !(t>>22)

      // hihat TODO improve/golf envelope
      + (t % 100 * t & 128) * Math.min(.2, (1e1 / ((t>>3) % 512)))

      // enable hihat after t>>19
      * !!(t>>19)

      // arpeggio
      + s(
        (t>>17) % 2 ? '027' : '037',  // melody
        1,                            // octave
        13 - 3 * (t>>20) % 12,        // rate
        4                             // melody length
      ) / K

      // enable arpeggio after t>>20
      * !!(t>>20)
    ) * (
      // fade out
      X = Math.min(1, Math.max(1e-9, 1e1-t++/5e5))
    ) / 200
  );

// gfx
g = c.getContext`webgl`;
P = g.createProgram();

// NOTE: 2nd argument to drawArrays used to be 0, but undefined works
r = time => g.drawArrays(g.TRIANGLE_FAN,
  // x-res, y-res, time (s), fade out
  g.uniform4f(g.getUniformLocation(P, 'a'), c.width, c.height, time / 1e3, X),
  3,
  // 1, 0, kick envelope, unused
  g.uniform4f(g.getUniformLocation(P, 'b'), 1, 0, .2/K, requestAnimationFrame(r))
);

// vertex shader
g.shaderSource(S=g.createShader(g.VERTEX_SHADER), require("./vertex.glsl"));
g.compileShader(S);g.attachShader(P,S);

// fragment shader
g.shaderSource(S=g.createShader(g.FRAGMENT_SHADER), require("./fragment.glsl"));
g.compileShader(S);g.attachShader(P,S);

// Log compilation errors
// if (!g.getShaderParameter(S, 35713)) { // COMPILE_STATUS = 35713
//   throw g.getShaderInfoLog(S);
// }

g.bindBuffer(g.ARRAY_BUFFER, g.createBuffer(c.parentElement.style.margin = 0));
// 1st argument to enableVertexAttribArray used to be 0, but undefined works
// 1st argument to vertexAttribPointer used to be 0, but undefined works
g.vertexAttribPointer(
  g.enableVertexAttribArray(
    g.bufferData(g.ARRAY_BUFFER, Int8Array.of(-3, 1, 1, -3, 1, 1), g.STATIC_DRAW)
  ),
2, g.BYTE, r(c.style.height = '100vh'), g.linkProgram(P), g.useProgram(P));
<|endoftext|>


<commit_before>/* eslint-disable no-unused-vars */
import vex from './vex';

import registerServiceWorker from './registerServiceWorker';
import './index.css';
import {createCells, restart, tick} from './color-automata';

import WasmLoader from './wasm-loader';

registerServiceWorker();

// console and debug settings
const noop = () => {};
const originalConsole = {log: console.log, time: console.time, timeEnd: console.timEnd};
const dummyConsole = {log: noop, time: noop, timeEnd: noop};

let currentIntervalId;
let wasm;

const resetCanvas = (canvas, configuration) => {
  const {width, height} = configuration;
  console.log('Resetting canvas to match grid configuration: ', width, height);
  if (canvas.width !== width || canvas.height !== height) {
      canvas.width = width;
      canvas.height = height;
  }
}

const wasmRenderTapestry = (configuration) => {
  console.log('Running the WASM implementation!');
  console.log('Window size: ', window.innerWidth, window.innerHeight);
  const canvas = document.createElement('canvas');

  resetCanvas(canvas, configuration);
  const {width, height} = configuration;
  console.time('initialization');
  const tapestry = new wasm.Tapestry(width, height);
  console.timeEnd('initialization');

  const displayCanvas = document.getElementById('displayCanvas');
  resetCanvas(displayCanvas, {width: window.innerWidth, height: window.innerHeight})

  const context = canvas.getContext('2d');
  const displayContext = displayCanvas.getContext('2d');
  displayContext.scale(scalingFactorX, scalingFactorY);

  currentIntervalId = setInterval(() => {
    console.time('wasm extract image');
    const tile = tapestry.fullImage();
    console.timeEnd('wasm extract image');

    console.time('canvas put image data');
    const imageData = new ImageData(new Uint8ClampedArray(tile), width, height);
    context.putImageData(imageData, 0, 0);
    console.timeEnd('canvas put image data');

    console.time('canvas scale other image');
    displayContext.drawImage(canvas, 0, 0);
    console.timeEnd('canvas scale other image');

    console.time('wasm compute image');
    tapestry.tick();
    console.timeEnd('wasm compute image');
  }, 1000 / 30);
}

const jsRenderTapestry = (configuration) => {
  // extend canvas to full screen
  const displayCanvas = document.getElementById("displayCanvas");
  const context = displayCanvas.getContext("2d");
  context.canvas.width  = window.innerWidth;
  context.canvas.height = window.innerHeight;
  const displayWidth = displayCanvas.width;
  const displayHeight = displayCanvas.height;

  const {width: gridWidth, height: gridHeight} = configuration;
  console.log('Running the pure JS implementation!');
  console.log('Window size: ', displayWidth, displayHeight);
  console.log('Grid size: ', gridWidth, gridHeight);

  const cellSize = {
    width: displayWidth / gridWidth,
    height: displayHeight / gridHeight
  };

  const options = {
    ease: 0.67,
    velMax: 255,
    minDist: 8,
    minDistSquare: 64,
    sepNormMag: 4
  }

  const list = createCells(cellSize, gridWidth, gridHeight);

  displayCanvas.addEventListener("click", restart(list), false);

  // we aim for 30 fps
  currentIntervalId = window.setInterval(tick(list, cellSize, context, options), 1000/30);
}

const url = new URL(window.location);

const scalingFactorX = Math.max(5, Math.floor(window.innerWidth / 1024));
const scalingFactorY = Math.max(5, Math.floor(window.innerHeight / 1024));

const tapestryConfiguration = {
  width: Math.floor(window.innerWidth / scalingFactorX),
  height: Math.floor(window.innerHeight / scalingFactorY),
  implementation: url.searchParams.get('pureJS') ? 'js' : 'wasm',
  debug: false
}

// TODO: configure cell size instead of choosing width and height
// that way there's no distortion and you can play better with granularity

const openConfigurationModal = () => {
  vex.dialog.buttons.YES.text = 'Reset';
  vex.dialog.open({
    message: `You are looking at a combination between a cellular automaton and a flocking algorithm that yields a color tapestry.
      Use the controls below to reset the tapestry to a different configuration.`,
    input: `

          Grid size
          <input name="height" type="number" value="${tapestryConfiguration.height}"></input>
          <input name="width" type="number" value="${tapestryConfiguration.width}"></input>

          Implementation
          <input type="radio" name="implementation" value="js" ${tapestryConfiguration.implementation === 'js' ? 'checked' : ''} />JS
          <input type="radio" name="implementation" value="wasm" ${tapestryConfiguration.implementation === 'wasm' ? 'checked' : ''} />WASM

          <input type="checkbox" name="debug" ${tapestryConfiguration.debug ? 'checked' : ''} />Console debugging

 Inspired by Rectangleworld.
      More details on on GitHub!

    `,
    callback: (data) => {
      startTapestry(extractConfiguration(data));
    }
  });
}

const extractConfiguration = (data) => {
  data.width = Number(data.width);
  data.height = Number(data.height);
  console.log(Object.assign(tapestryConfiguration, data));
  return tapestryConfiguration;
}

const startTapestry = (configuration) => {
  clearInterval(currentIntervalId);
  // if debugging is enabled, use the console otherwise don't
  Object.assign(console, configuration.debug ? originalConsole : dummyConsole);

  if (configuration.implementation === 'js') {
    jsRenderTapestry(configuration);
  } else {
    wasmRenderTapestry(configuration);
  }
}

window.onload = () => {
  WasmLoader({wasmBinaryFile: 'tapestry.wasm'}).then(WasmModule => {
    WasmModule.addOnPostRun(() => {
      wasm = WasmModule;
      const octocat = document.getElementsByClassName('github-corner')[0];
      octocat.addEventListener('click', (e) => {
        e.preventDefault();
        openConfigurationModal();
      });
      startTapestry(tapestryConfiguration);
    });
  });
}
<commit_msg>Replace url parameter with feature detection for initial run<commit_after>/* eslint-disable no-unused-vars */
import vex from './vex';

import registerServiceWorker from './registerServiceWorker';
import './index.css';
import {createCells, restart, tick} from './color-automata';

import WasmLoader from './wasm-loader';

registerServiceWorker();

// console and debug settings
const noop = () => {};
const originalConsole = {log: console.log, time: console.time, timeEnd: console.timEnd};
const dummyConsole = {log: noop, time: noop, timeEnd: noop};

let currentIntervalId;
let wasm;

const resetCanvas = (canvas, configuration) => {
  const {width, height} = configuration;
  console.log('Resetting canvas to match grid configuration: ', width, height);
  if (canvas.width !== width || canvas.height !== height) {
      canvas.width = width;
      canvas.height = height;
  }
}

const wasmRenderTapestry = (configuration) => {
  console.log('Running the WASM implementation!');
  console.log('Window size: ', window.innerWidth, window.innerHeight);
  const canvas = document.createElement('canvas');

  resetCanvas(canvas, configuration);
  const {width, height} = configuration;
  console.time('initialization');
  const tapestry = new wasm.Tapestry(width, height);
  console.timeEnd('initialization');

  const displayCanvas = document.getElementById('displayCanvas');
  resetCanvas(displayCanvas, {width: window.innerWidth, height: window.innerHeight})

  const context = canvas.getContext('2d');
  const displayContext = displayCanvas.getContext('2d');
  displayContext.scale(scalingFactorX, scalingFactorY);

  currentIntervalId = setInterval(() => {
    console.time('wasm extract image');
    const tile = tapestry.fullImage();
    console.timeEnd('wasm extract image');

    console.time('canvas put image data');
    const imageData = new ImageData(new Uint8ClampedArray(tile), width, height);
    context.putImageData(imageData, 0, 0);
    console.timeEnd('canvas put image data');

    console.time('canvas scale other image');
    displayContext.drawImage(canvas, 0, 0);
    console.timeEnd('canvas scale other image');

    console.time('wasm compute image');
    tapestry.tick();
    console.timeEnd('wasm compute image');
  }, 1000 / 30);
}

const jsRenderTapestry = (configuration) => {
  // extend canvas to full screen
  const displayCanvas = document.getElementById("displayCanvas");
  const context = displayCanvas.getContext("2d");
  context.canvas.width  = window.innerWidth;
  context.canvas.height = window.innerHeight;
  const displayWidth = displayCanvas.width;
  const displayHeight = displayCanvas.height;

  const {width: gridWidth, height: gridHeight} = configuration;
  console.log('Running the pure JS implementation!');
  console.log('Window size: ', displayWidth, displayHeight);
  console.log('Grid size: ', gridWidth, gridHeight);

  const cellSize = {
    width: displayWidth / gridWidth,
    height: displayHeight / gridHeight
  };

  const options = {
    ease: 0.67,
    velMax: 255,
    minDist: 8,
    minDistSquare: 64,
    sepNormMag: 4
  }

  const list = createCells(cellSize, gridWidth, gridHeight);

  displayCanvas.addEventListener("click", restart(list), false);

  // we aim for 30 fps
  currentIntervalId = window.setInterval(tick(list, cellSize, context, options), 1000/30);
}

const scalingFactorX = Math.max(5, Math.floor(window.innerWidth / 1024));
const scalingFactorY = Math.max(5, Math.floor(window.innerHeight / 1024));

const isWebAssemblySupported = () => typeof window.WebAssembly === 'object';

const tapestryConfiguration = {
  width: Math.floor(window.innerWidth / scalingFactorX),
  height: Math.floor(window.innerHeight / scalingFactorY),
  implementation: isWebAssemblySupported() ? 'wasm' : 'js',
  debug: false
}

// TODO: configure cell size instead of choosing width and height
// that way there's no distortion and you can play better with granularity

const openConfigurationModal = () => {
  vex.dialog.buttons.YES.text = 'Reset';
  // TODO: currently running configuration instead of description
  vex.dialog.open({
    message: `You are looking at a combination between a cellular automaton and a flocking algorithm that yields a color tapestry.
      Use the controls below to reset the tapestry to a different configuration.`,
    input: `

          Cell size
          <input name="height" type="number" value="${tapestryConfiguration.height}"></input>
          <input name="width" type="number" value="${tapestryConfiguration.width}"></input>

          Implementation
          <input type="radio" name="implementation" value="js" ${tapestryConfiguration.implementation === 'js' ? 'checked' : ''} />JS
          <input type="radio" name="implementation" value="wasm" ${tapestryConfiguration.implementation === 'wasm' ? 'checked' : ''} />WASM

          <input type="checkbox" name="debug" ${tapestryConfiguration.debug ? 'checked' : ''} />Console debugging

 Inspired by Rectangleworld.
      More details on on GitHub!

    `,
    callback: (data) => {
      startTapestry(extractConfiguration(data));
    }
  });
}

const extractConfiguration = (data) => {
  data.width = Number(data.width);
  data.height = Number(data.height);
  console.log(Object.assign(tapestryConfiguration, data));
  return tapestryConfiguration;
}

const startTapestry = (configuration) => {
  clearInterval(currentIntervalId);
  // if debugging is enabled, use the console otherwise don't
  Object.assign(console, configuration.debug ? originalConsole : dummyConsole);

  if (configuration.implementation === 'js') {
    jsRenderTapestry(configuration);
  } else {
    wasmRenderTapestry(configuration);
  }
}

window.onload = () => {
  WasmLoader({wasmBinaryFile: 'tapestry.wasm'}).then(WasmModule => {
    WasmModule.addOnPostRun(() => {
      wasm = WasmModule;
      const octocat = document.getElementsByClassName('github-corner')[0];
      octocat.addEventListener('click', (e) => {
        e.preventDefault();
        openConfigurationModal();
      });
      startTapestry(tapestryConfiguration);
    });
  });
}
<|endoftext|>


<commit_before>var React = require("react");
var _ = require("lodash");

// TableSorter React Component
var TableSorter = module.exports = React.createClass({
  getInitialState: function() {
    return {
      items: this.props.initialItems || [],
      sort: this.props.config.sort || { column: "", order: "" },
      columns: this.props.config.columns
    };
  },
  componentWillReceiveProps: function(nextProps) {
    // Load new data when the dataSource property changes.
    if (nextProps.dataSource != this.props.dataSource || nextProps.data != this.props.data) {
      this.loadData(nextProps.dataSource, nextProps.data);
    }
  },
  componentWillMount: function() {
    this.loadData(this.props.dataSource, this.props.data);
  },
  loadData: function(dataSource, data) {
    if (!data){
      request = new XMLHttpRequest();
      request.open('GET', dataSource, true);

      request.onload = function() {
        if (request.status >= 200 && request.status < 400){
          // Success!
          data = JSON.parse(request.responseText);
          data.forEach(function(item){
          for (var key in item) {
              // allows text or react components
              if(!item[key]){
                item[key] = {"text":"none"}
              }
              else if(typeof item[key] !== 'object' || item[key].props) {
                item[key] = {"text":item[key]}
              }
            }
          });
          console.log("Received data");
          this.setState({items: data});
        } else {
          // We reached our target server, but it returned an error
          console.log("Error loading JSON");
        }
      }.bind(this);

      request.onerror = function() {
        // There was a connection error of some sort
        console.log("Connection error. Could not retrieve JSON");
      };

      request.send();
    } else{
      data.forEach(function(item){
      for (var key in item) {
          if(!item[key]){
            item[key] = {"text":"none"}
          }
          else if(typeof item[key] !== 'object' || item[key].props) {
            item[key] = {"text":item[key]}
          }
        }
      });
      this.setState({items: data});
   }
  },
  handleFilterTextChange: function(column) {
    return function(newValue) {
      var obj = this.state.columns;
      obj[column].filterText = newValue;

      // Since we have already mutated the state, just call forceUpdate().
      // Ideally we'd copy and setState or use an immutable data structure.
      this.forceUpdate();
    }.bind(this);
  },
  columnNames: function() {
     return Object.keys(this.state.columns);
  },
  sortColumn: function(column) {
    return function(event) {
      var newSortOrder = (this.state.sort.order == "asc") ? "desc" : "asc";

      if (this.state.sort.column != column) {
          newSortOrder = this.state.columns[column].defaultSortOrder;
      }

      this.setState({sort: { column: column, order: newSortOrder }});
    }.bind(this);
  },
  sortClass: function(column) {
    var ascOrDesc = (this.state.sort.order == "asc") ? "headerSortAsc" : "headerSortDesc";
    return (this.state.sort.column == column) ? ascOrDesc : "";
  },
  render: function() {
    var rows = [];
	var operandMatch;
    var columnNames = this.columnNames();
    var filters = {};

      // Inequality function map for the filtering
    var operators = {
      "<": function(x, y) { return x < y; },
      "<=": function(x, y) { return x <= y; },
      ">": function(x, y) { return x > y; },
      ">=": function(x, y) { return x >= y; },
      "==": function(x, y) { return x == y; }
    };

    var operandRegex = /^((?:(?:[<>]=?)|==))\s?([-]?\d+(?:\.\d+)?)$/;

    columnNames.forEach(function(column) {
      var filterText = this.state.columns[column].filterText;
      filters[column] = null;

      if (filterText.length > 0) {
        operandMatch = operandRegex.exec(filterText);
        if (operandMatch && operandMatch.length == 3) {
          filters[column] = function(match) { return function(x) { return operators[match[1]](x, match[2]); }; }(operandMatch);
        } else {
          filters[column] = function(x) {
            return (x.toString().toLowerCase().indexOf(filterText.toLowerCase()) > -1);
          };
        }
      }
    }, this);

    var filteredItems = _.filter(this.state.items, function(item) {
      return _.every(columnNames, function(c) {
        return (!filters[c] || filters[c](item[c].text));
      }, this);
    }, this);

    var sortedItems = _.sortBy(filteredItems, this.state.sort.column);

    if (this.state.sort.order === "desc") sortedItems.reverse();

    var headerExtra = function() {
      return columnNames.map(function(c, i) {
        return ({this.state.columns[c].name}	);
      }, this);
    }.bind(this);

    var cell = function(x) {
      return columnNames.map(function(c, i) {
        //custom link defined in data
        if (x[c].link){
          return {x[c].text}	;
        // link defined in config for entire column
        } else if (this.props.config.columns[c].link) {
         var link = this.props.config.columns[c].link.replace("{cell}", x[c].text);
          return {x[c]}	;
        // simple cell
        } else {
          return {x[c].text}	;
        }
      }, this);
    }.bind(this);

    var keyIndex = 0;
    sortedItems.forEach(function(item, idx) {
      var headerRepeat = parseInt(this.props.headerRepeat, 10);
      if ((this.props.headerRepeat > 0) &&
          (idx > 0) &&
          (idx % this.props.headerRepeat === 0)) {

          rows.push (

              { headerExtra() }

          )
      }

      rows.push(

          { cell(item) }

      );
      keyIndex++;
    }.bind(this));

    var filterLink = function(column) {
      return {
        value: this.state.columns[column].filterText,
        requestChange: this.handleFilterTextChange(column)
      };
    }.bind(this);

    var header = columnNames.map(function(c, i) {
      return {this.state.columns[c].name}	;
    }, this);

    var filterInputs = columnNames.map(function(c, i) {
      return <input type="text" valueLink={filterLink(c)} />	;
    }, this);

    return (

            { header }

            { filterInputs }

          { rows }

    );
  }
});
<commit_msg>excluede white space changes<commit_after>var React = require("react");
var _ = require("lodash");

// TableSorter React Component
var TableSorter = module.exports = React.createClass({
  getInitialState: function() {
    return {
      items: this.props.initialItems || [],
      sort: this.props.config.sort || { column: "", order: "" },
      columns: this.props.config.columns
    };
  },
  componentWillReceiveProps: function(nextProps) {
    // Load new data when the dataSource property changes.
    if (nextProps.dataSource != this.props.dataSource || nextProps.data != this.props.data) {
      this.loadData(nextProps.dataSource, nextProps.data);
    }
  },
  componentWillMount: function() {
    this.loadData(this.props.dataSource, this.props.data);
  },
  loadData: function(dataSource, data) {
    if (!data){
      request = new XMLHttpRequest();
      request.open('GET', dataSource, true);

      request.onload = function() {
        if (request.status >= 200 && request.status < 400){
          // Success!
          data = JSON.parse(request.responseText);
          data.forEach(function(item){
          for (var key in item) {
              // allows text or react components
              if(!item[key]){
                item[key] = {"text":"none"}
              }
              else if(typeof item[key] !== 'object' || item[key].props) {
                item[key] = {"text":item[key]}
              }
            }
          });
          console.log("Received data");
          this.setState({items: data});
        } else {
          // We reached our target server, but it returned an error
          console.log("Error loading JSON");
        }
      }.bind(this);

      request.onerror = function() {
        // There was a connection error of some sort
        console.log("Connection error. Could not retrieve JSON");
      };

      request.send();
    } else{
      data.forEach(function(item){
      for (var key in item) {
          if(!item[key]){
            item[key] = {"text":"none"}
          }
          else if(typeof item[key] !== 'object' || item[key].props) {
            item[key] = {"text":item[key]}
          }
        }
      });
      this.setState({items: data});
   }
  },
  handleFilterTextChange: function(column) {
    return function(newValue) {
      var obj = this.state.columns;
      obj[column].filterText = newValue;

      // Since we have already mutated the state, just call forceUpdate().
      // Ideally we'd copy and setState or use an immutable data structure.
      this.forceUpdate();
    }.bind(this);
  },
  columnNames: function() {
     return Object.keys(this.state.columns);
  },
  sortColumn: function(column) {
    return function(event) {
      var newSortOrder = (this.state.sort.order == "asc") ? "desc" : "asc";

      if (this.state.sort.column != column) {
          newSortOrder = this.state.columns[column].defaultSortOrder;
      }

      this.setState({sort: { column: column, order: newSortOrder }});
    }.bind(this);
  },
  sortClass: function(column) {
    var ascOrDesc = (this.state.sort.order == "asc") ? "headerSortAsc" : "headerSortDesc";
    return (this.state.sort.column == column) ? ascOrDesc : "";
  },
  render: function() {
    var rows = [];

    var columnNames = this.columnNames();
    var filters = {};

      // Inequality function map for the filtering
    var operators = {
      "<": function(x, y) { return x < y; },
      "<=": function(x, y) { return x <= y; },
      ">": function(x, y) { return x > y; },
      ">=": function(x, y) { return x >= y; },
      "==": function(x, y) { return x == y; }
    };

    var operandRegex = /^((?:(?:[<>]=?)|==))\s?([-]?\d+(?:\.\d+)?)$/;

    var operandMatch;

    columnNames.forEach(function(column) {
      var filterText = this.state.columns[column].filterText;
      filters[column] = null;

      if (filterText.length > 0) {
        operandMatch = operandRegex.exec(filterText);
        if (operandMatch && operandMatch.length == 3) {
          filters[column] = function(match) { return function(x) { return operators[match[1]](x, match[2]); }; }(operandMatch);
        } else {
          filters[column] = function(x) {
            return (x.toString().toLowerCase().indexOf(filterText.toLowerCase()) > -1);
          };
        }
      }
    }, this);

    var filteredItems = _.filter(this.state.items, function(item) {
      return _.every(columnNames, function(c) {
        return (!filters[c] || filters[c](item[c].text));
      }, this);
    }, this);

    var sortedItems = _.sortBy(filteredItems, this.state.sort.column);

    if (this.state.sort.order === "desc") sortedItems.reverse();

    var headerExtra = function() {
      return columnNames.map(function(c, i) {
        return ({this.state.columns[c].name}	);
      }, this);
    }.bind(this);

    var cell = function(x) {
      return columnNames.map(function(c, i) {
        //custom link defined in data
        if (x[c].link){
          return {x[c].text}	;
        // link defined in config for entire column
        } else if (this.props.config.columns[c].link) {
         var link = this.props.config.columns[c].link.replace("{cell}", x[c].text);
          return {x[c]}	;
        // simple cell
        } else {
          return {x[c].text}	;
        }
      }, this);
    }.bind(this);

    var keyIndex = 0;
    sortedItems.forEach(function(item, idx) {
      var headerRepeat = parseInt(this.props.headerRepeat, 10);
      if ((this.props.headerRepeat > 0) &&
          (idx > 0) &&
          (idx % this.props.headerRepeat === 0)) {

          rows.push (

              { headerExtra() }

          )
      }

      rows.push(

          { cell(item) }

      );
      keyIndex++;
    }.bind(this));

    var filterLink = function(column) {
      return {
        value: this.state.columns[column].filterText,
        requestChange: this.handleFilterTextChange(column)
      };
    }.bind(this);

    var header = columnNames.map(function(c, i) {
      return {this.state.columns[c].name}	;
    }, this);

    var filterInputs = columnNames.map(function(c, i) {
      return <input type="text" valueLink={filterLink(c)} />	;
    }, this);

    return (

            { header }

            { filterInputs }

          { rows }

    );
  }
});
<|endoftext|>


<commit_before>const React = require('react');
const {QRCode, QRErrorCorrectLevel, QRCapacityTable, UTF8Array} = require('./qrcode');

const SUPPORT_CANVAS = !!document.createElement('canvas').getContext;
const SUPPORT_SVG = !!(document.createElementNS && document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);

function getQRVersion(text, ecl) {
    let c = UTF8Array(text).length,
        eci = [QRErrorCorrectLevel.L, QRErrorCorrectLevel.M, QRErrorCorrectLevel.Q, QRErrorCorrectLevel.H],
        capacity = 0, version;

    //figure out what version can hold the amount of text
    for (var i=0,j=QRCapacityTable.length;i<j;i++) {
        capacity = QRCapacityTable[i][eci[ecl]];
        if (c < QRCapacityTable[i][eci[ecl]]) {
            version = i + 1;
            break;
        }
    }

    if (!version) {
        version = QRCapacityTable.length - 1;
    }

    if (capacity < c) {
        throw new Error("Content too long");
    }

    return version;
}

class ReactQRCode extends React.Component {
    render() {
        let renderer = this.props.render || this.props.renderer;
        if (!renderer || renderer === 'auto') {
            renderer = SUPPORT_SVG ? 'svg' : (SUPPORT_CANVAS ? 'canvas' : '');
        }
        if (renderer === 'canvas') {
            return <CanvasQRCode {...this.props} />;
        } else {
            return <SvgQRCode {...this.props} />;
        }
    }
}

class SvgQRCode extends React.Component {
    componentWillMount() {
        this.qrcode = new QRCode(getQRVersion(this.props.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
        this.qrcode.addData(this.props.content);
        this.qrcode.make();
    }
    componentWillReceiveProps(nextProps) {
        if (nextProps.content !== this.props.content) {
            this.qrcode = new QRCode(getQRVersion(nextProps.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
            this.qrcode.addData(nextProps.content);
            this.qrcode.make();
        }
    }

    render() {
        let props = this.props;
        let qrcode = this.qrcode;
        let count = qrcode.getModuleCount();
        let scale = +props.scale || 4;
        let margin = +props.margin || 20;
        let size = count * scale + margin * 2;

        let rects = [<rect key="background" x="0" y="0" width={size} height={size} style={{"fill":props.background, shapeRendering:"crispEdges"}}/>];

        let currenty = margin, currentx;
        for (let row = 0; row < count; row++) {
            currentx = margin;
            for (let col = 0; col < count; col++) {
                if (qrcode.isDark(row, col)) {
                    rects.push(<rect key={(row + '-' + col)} x={currentx} y={currenty} width={scale} height={scale} style={{"fill":props.foreground, shapeRendering:"crispEdges"}}/>);
                }
                currentx += scale;
            }
            currenty += scale;
        }

        return <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width={size} height={size}>{rects}</svg>;
    }
}

class CanvasQRCode extends React.Component {
    componentWillMount() {
        this.qrcode = new QRCode(getQRVersion(this.props.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
        this.qrcode.addData(this.props.content);
        this.qrcode.make();
    }
    componentWillReceiveProps(nextProps) {
        if (nextProps.content !== this.props.content) {
            this.qrcode = new QRCode(getQRVersion(nextProps.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
            this.qrcode.addData(nextProps.content);
            this.qrcode.make();
        }
    }

    componentDidMount() {
        this.draw();
    }

    componentDidUpdate() {
        this.draw();
    }

    draw() {
        if (!this.canvas) return;
        let props = this.props;
        let qrcode = this.qrcode;
        let canvas = this.canvas;
        let ctx = canvas.getContext('2d');
        let count = qrcode.getModuleCount();
        let scale = +props.scale || 4;
        let margin = +props.margin || 20;
        let size = count * scale + margin * 2;

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        canvas.style.height = canvas.height = size;
        canvas.style.width = canvas.width = size;
        ctx.fillStyle = props.background;
        ctx.fillRect(0, 0, size, size);

        ctx.fillStyle = props.foreground;
        let currenty = margin, currentx;
        // draw in the canvas
        for (let row = 0; row < count; row++) {
            currentx = margin;
            for (let col = 0; col < count; col++) {
                if (qrcode.isDark(row, col)) {
                    ctx.fillRect(currentx, currenty, scale, scale);
                }
                currentx += scale;
            }
            currenty += scale;
        }
    }

    render() {
        return <canvas ref={(ref) => { this.canvas = ref; }} />;
    }
}

ReactQRCode.defaultProps = {
    renderer: "canvas",
    content: "",
    scale: 4,
    margin: 20,
    background: "white",
    foreground: "black"
};

ReactQRCode.propTypes = {
    renderer: React.PropTypes.oneOf(['canvas', 'svg', 'auto']),
    content: React.PropTypes.string,
    scale: React.PropTypes.oneOfType([
        React.PropTypes.number,
        React.PropTypes.string
    ]),
    margin: React.PropTypes.oneOfType([
        React.PropTypes.number,
        React.PropTypes.string
    ]),
    background: React.PropTypes.string,
    foreground: React.PropTypes.string
};

module.exports = ReactQRCode;
<commit_msg>fix #2<commit_after>const React = require('react');
const {QRCode, QRErrorCorrectLevel, QRCapacityTable, UTF8Array} = require('./qrcode');

function getQRVersion(text, ecl) {
    let c = UTF8Array(text).length,
        eci = [QRErrorCorrectLevel.L, QRErrorCorrectLevel.M, QRErrorCorrectLevel.Q, QRErrorCorrectLevel.H],
        capacity = 0, version;

    //figure out what version can hold the amount of text
    for (var i=0,j=QRCapacityTable.length;i<j;i++) {
        capacity = QRCapacityTable[i][eci[ecl]];
        if (c < QRCapacityTable[i][eci[ecl]]) {
            version = i + 1;
            break;
        }
    }

    if (!version) {
        version = QRCapacityTable.length - 1;
    }

    if (capacity < c) {
        throw new Error("Content too long");
    }

    return version;
}

class ReactQRCode extends React.Component {
    render() {
        let renderer = this.props.render || this.props.renderer;
        if (!renderer || renderer === 'auto') {
            renderer = 'svg';
        }
        if (renderer === 'canvas') {
            return <CanvasQRCode {...this.props} />;
        } else {
            return <SvgQRCode {...this.props} />;
        }
    }
}

class SvgQRCode extends React.Component {
    componentWillMount() {
        this.qrcode = new QRCode(getQRVersion(this.props.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
        this.qrcode.addData(this.props.content);
        this.qrcode.make();
    }
    componentWillReceiveProps(nextProps) {
        if (nextProps.content !== this.props.content) {
            this.qrcode = new QRCode(getQRVersion(nextProps.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
            this.qrcode.addData(nextProps.content);
            this.qrcode.make();
        }
    }

    render() {
        let props = this.props;
        let qrcode = this.qrcode;
        let count = qrcode.getModuleCount();
        let scale = +props.scale || 4;
        let margin = +props.margin || 20;
        let size = count * scale + margin * 2;

        let rects = [<rect key="background" x="0" y="0" width={size} height={size} style={{"fill":props.background, shapeRendering:"crispEdges"}}/>];

        let currenty = margin, currentx;
        for (let row = 0; row < count; row++) {
            currentx = margin;
            for (let col = 0; col < count; col++) {
                if (qrcode.isDark(row, col)) {
                    rects.push(<rect key={(row + '-' + col)} x={currentx} y={currenty} width={scale} height={scale} style={{"fill":props.foreground, shapeRendering:"crispEdges"}}/>);
                }
                currentx += scale;
            }
            currenty += scale;
        }

        return <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width={size} height={size}>{rects}</svg>;
    }
}

class CanvasQRCode extends React.Component {
    componentWillMount() {
        this.qrcode = new QRCode(getQRVersion(this.props.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
        this.qrcode.addData(this.props.content);
        this.qrcode.make();
    }
    componentWillReceiveProps(nextProps) {
        if (nextProps.content !== this.props.content) {
            this.qrcode = new QRCode(getQRVersion(nextProps.content, QRErrorCorrectLevel.M), QRErrorCorrectLevel.M);
            this.qrcode.addData(nextProps.content);
            this.qrcode.make();
        }
    }

    componentDidMount() {
        this.draw();
    }

    componentDidUpdate() {
        this.draw();
    }

    draw() {
        if (!this.canvas) return;
        let props = this.props;
        let qrcode = this.qrcode;
        let canvas = this.canvas;
        let ctx = canvas.getContext('2d');
        let count = qrcode.getModuleCount();
        let scale = +props.scale || 4;
        let margin = +props.margin || 20;
        let size = count * scale + margin * 2;

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        canvas.style.height = canvas.height = size;
        canvas.style.width = canvas.width = size;
        ctx.fillStyle = props.background;
        ctx.fillRect(0, 0, size, size);

        ctx.fillStyle = props.foreground;
        let currenty = margin, currentx;
        // draw in the canvas
        for (let row = 0; row < count; row++) {
            currentx = margin;
            for (let col = 0; col < count; col++) {
                if (qrcode.isDark(row, col)) {
                    ctx.fillRect(currentx, currenty, scale, scale);
                }
                currentx += scale;
            }
            currenty += scale;
        }
    }

    render() {
        return <canvas ref={(ref) => { this.canvas = ref; }} />;
    }
}

ReactQRCode.defaultProps = {
    renderer: "canvas",
    content: "",
    scale: 4,
    margin: 20,
    background: "white",
    foreground: "black"
};

ReactQRCode.propTypes = {
    renderer: React.PropTypes.oneOf(['canvas', 'svg', 'auto']),
    content: React.PropTypes.string,
    scale: React.PropTypes.oneOfType([
        React.PropTypes.number,
        React.PropTypes.string
    ]),
    margin: React.PropTypes.oneOfType([
        React.PropTypes.number,
        React.PropTypes.string
    ]),
    background: React.PropTypes.string,
    foreground: React.PropTypes.string
};

module.exports = ReactQRCode;
<|endoftext|>