<commit_before>var textSearchBar = {
    bindings: {},
    templateUrl: '/static/js/components/text-search/text-search-bar/text-search-bar.html',
    controller: ['$interpolate', '$location', '$window', '$timeout', '$http', 'search', function($interpolate, $location, $window, $timeout, $http, search) {
        var ctrl = this;
        ctrl.search = search;

        ctrl.$onInit = function() {
            ctrl.query = '';
            ctrl.submitted = false; // when form is submitted this flag is set; when its content is edited it is cleared

            // check if the url contains a query when the controller is first created and initiate a search if necessary
            if ($location.url().indexOf("/search?q=") > -1) {
                search.search($location.search().q); // a search result page, launch a new search
            }
            ctrl.randomExamples = ctrl.getRandomSearchExamples();
        };

        /**
         * Watch search.query and if it's different from ctrl.query, check, who changed and sync if necessary
         */
        ctrl.$doCheck = function() {
            if (search.query != ctrl.query) {
                if (search.status == 'in progress') {
                    // this is a result of search.search() call => sync form input with search.query
                    ctrl.query = search.query;
                }
            }
        };

        /**
         * Called when user changes the value in query string.
         */
        ctrl.autocomplete = function(query) {
            return search.autocomplete(query).then(
                function(response) {
                    // make exact matches appear in the top of the list
                    var exactMatches = [], fuzzyMatches = [];
                    for (var i=0; i < response.data.suggestions.length; i++) {
                        var suggestion = response.data.suggestions[i].suggestion;
                        if (suggestion.indexOf(ctrl.query) === 0) exactMatches.push(suggestion);
                        else fuzzyMatches.push(suggestion);
                    }

                    return exactMatches.concat(fuzzyMatches);
                },
                function(response) {
                    return [];
                }
            );
        };

        /**
         * Called when the form is submitted. If request is invalid, just display error and die, else run search.
         */
        ctrl.submitQuery = function() {
            ctrl.queryForm.text.$invalid ? ctrl.submitted = true : search.search(ctrl.query);
        };

        /**
         * Sends ajax to text-search help page, copy-pastes its content to the modal,
         * copies over that modal to  and opens it.
         */
        ctrl.openTextSearchHelpModal = function() {
            // request text search help from the backend
            $http.get('/help/text-search').then(
                function(result) {
                    // copy-paste help content from text search help page to the modal
                    var $helpContents = $( $(result.data).find('#help-content').get(0) );
                    $helpContents.find('h1').get(0).remove(); // remove page header - we already have a header in modal

                    // make search examples clickable
                    $helpContents.find('code').each(function() {
                        var $this = $(this);
                        var link = '' + $this.html() + '';
                        $this.html(link);
                    });

                    // style table
                    $helpContents.find('table').addClass('table table-bordered table-responsive');

                    // remove fa-links
                    $helpContents.find('a').each(function() {
                        $(this).find('i.fa-link').remove();
                    });

                    // copy over help contents to the modal
                    $('#text-search-help-modal-parent #modal-body').html($helpContents.html());

                    // move modal from our component's html to
                    $('#text-search-help-modal-parent').detach().appendTo('body');

                    // open modal; possible options: { backdrop: true, keyboard: true, show: true }
                    $('#text-search-help-modal-parent').modal();
                }
            );
        };

        /**
         * Get random text search examples from each search category.
         */
        ctrl.getRandomSearchExamples = function() {
            examples = {
              'gene': [
                    {
                        'label': 'human HOTAIR',
                        'search_string': '"HOTAIR" homo sapiens',
                    },
                    {
                        'label': 'human RN7SL',
                        'search_string': 'RN7SL homo sapiens',
                    },
                    {
                        'label': 'KCNQ1OT1',
                        'search_string': 'KCNQ1OT1',
                    },
                ],
                'species': [
                    {
                        'label': 'Homo sapiens',
                        'search_string': 'TAXONOMY:"9606"',
                    },
                    {
                        'label': 'Mus musculus',
                        'search_string': 'TAXONOMY:"10090"',
                    },
                    {
                        'label': 'D. melanogaster',
                        'search_string': 'TAXONOMY:"7227"',
                    },
                    {
                        'label': 'primates',
                        'search_string': 'tax_string:"primates"',
                    },
                    {
                        'label': 'Escherichia coli',
                        'search_string': 'Escherichia coli',
                    },
                    {
                        'label': 'Alphaproteobacteria',
                        'search_string': 'tax_string:"Alphaproteobacteria"',
                    },
                ],
                'rna_type': [
                    {
                        'label': 'lncRNA',
                        'search_string': 'rna_type:"lncRNA"',
                    },
                    {
                        'label': 'miRNA',
                        'search_string': 'rna_type:"miRNA"',
                    },
                    {
                        'label': 'snoRNA',
                        'search_string': 'rna_type:"snoRNA"',
                    },
                ],
                'expert_db': [
                    {
                        'label': 'miRBase',
                        'search_string': 'expert_db:"miRBase"',
                    },
                    {
                        'label': 'FlyBase',
                        'search_string': 'expert_db:"FlyBase"',
                    },
                    {
                        'label': 'GtRNAdb',
                        'search_string': 'expert_db:"GtRNAdb"',
                    },
                    {
                        'label': 'HGNC',
                        'search_string': 'expert_db:"HGNC"',
                    },
                    {
                        'label': 'LNCipedia',
                        'search_string': 'expert_db:"LNCipedia"',
                    },
                  ],
                    'accession': [
                    {
                        'label': 'ENSG00000228630',
                        'search_string': 'ENSG00000228630',
                    },
                    {
                        'label': '4V4Q',
                        'search_string': '4V4Q',
                    },
                    {
                        'label': 'hsa-mir-126',
                        'search_string': '"hsa-mir-126"',
                    },
                    {
                        'label': "PMID:27174936",
                        'search_string': 'pubmed:"27174936"',
                    },
                    {
                        'label': 'GO:0043410',
                        'search_string': '"GO:2000352"',
                    },
                  ]
            }
            randomExamples = new Array();
            Object.keys(examples).forEach(function(example_type) {
                randomExamples.push(examples[example_type][Math.floor(Math.random()*examples[example_type].length)]);
            });
            return randomExamples;
        }
    }]
};

angular.module('textSearch').component('textSearchBar', textSearchBar);
<commit_msg>Add TarBase to featured search examples<commit_after>var textSearchBar = {
    bindings: {},
    templateUrl: '/static/js/components/text-search/text-search-bar/text-search-bar.html',
    controller: ['$interpolate', '$location', '$window', '$timeout', '$http', 'search', function($interpolate, $location, $window, $timeout, $http, search) {
        var ctrl = this;
        ctrl.search = search;

        ctrl.$onInit = function() {
            ctrl.query = '';
            ctrl.submitted = false; // when form is submitted this flag is set; when its content is edited it is cleared

            // check if the url contains a query when the controller is first created and initiate a search if necessary
            if ($location.url().indexOf("/search?q=") > -1) {
                search.search($location.search().q); // a search result page, launch a new search
            }
            ctrl.randomExamples = ctrl.getRandomSearchExamples();
        };

        /**
         * Watch search.query and if it's different from ctrl.query, check, who changed and sync if necessary
         */
        ctrl.$doCheck = function() {
            if (search.query != ctrl.query) {
                if (search.status == 'in progress') {
                    // this is a result of search.search() call => sync form input with search.query
                    ctrl.query = search.query;
                }
            }
        };

        /**
         * Called when user changes the value in query string.
         */
        ctrl.autocomplete = function(query) {
            return search.autocomplete(query).then(
                function(response) {
                    // make exact matches appear in the top of the list
                    var exactMatches = [], fuzzyMatches = [];
                    for (var i=0; i < response.data.suggestions.length; i++) {
                        var suggestion = response.data.suggestions[i].suggestion;
                        if (suggestion.indexOf(ctrl.query) === 0) exactMatches.push(suggestion);
                        else fuzzyMatches.push(suggestion);
                    }

                    return exactMatches.concat(fuzzyMatches);
                },
                function(response) {
                    return [];
                }
            );
        };

        /**
         * Called when the form is submitted. If request is invalid, just display error and die, else run search.
         */
        ctrl.submitQuery = function() {
            ctrl.queryForm.text.$invalid ? ctrl.submitted = true : search.search(ctrl.query);
        };

        /**
         * Sends ajax to text-search help page, copy-pastes its content to the modal,
         * copies over that modal to  and opens it.
         */
        ctrl.openTextSearchHelpModal = function() {
            // request text search help from the backend
            $http.get('/help/text-search').then(
                function(result) {
                    // copy-paste help content from text search help page to the modal
                    var $helpContents = $( $(result.data).find('#help-content').get(0) );
                    $helpContents.find('h1').get(0).remove(); // remove page header - we already have a header in modal

                    // make search examples clickable
                    $helpContents.find('code').each(function() {
                        var $this = $(this);
                        var link = '' + $this.html() + '';
                        $this.html(link);
                    });

                    // style table
                    $helpContents.find('table').addClass('table table-bordered table-responsive');

                    // remove fa-links
                    $helpContents.find('a').each(function() {
                        $(this).find('i.fa-link').remove();
                    });

                    // copy over help contents to the modal
                    $('#text-search-help-modal-parent #modal-body').html($helpContents.html());

                    // move modal from our component's html to
                    $('#text-search-help-modal-parent').detach().appendTo('body');

                    // open modal; possible options: { backdrop: true, keyboard: true, show: true }
                    $('#text-search-help-modal-parent').modal();
                }
            );
        };

        /**
         * Get random text search examples from each search category.
         */
        ctrl.getRandomSearchExamples = function() {
            examples = {
              'gene': [
                    {
                        'label': 'human HOTAIR',
                        'search_string': '"HOTAIR" homo sapiens',
                    },
                    {
                        'label': 'human RN7SL',
                        'search_string': 'RN7SL homo sapiens',
                    },
                    {
                        'label': 'KCNQ1OT1',
                        'search_string': 'KCNQ1OT1',
                    },
                ],
                'species': [
                    {
                        'label': 'Homo sapiens',
                        'search_string': 'TAXONOMY:"9606"',
                    },
                    {
                        'label': 'Mus musculus',
                        'search_string': 'TAXONOMY:"10090"',
                    },
                    {
                        'label': 'D. melanogaster',
                        'search_string': 'TAXONOMY:"7227"',
                    },
                    {
                        'label': 'primates',
                        'search_string': 'tax_string:"primates"',
                    },
                    {
                        'label': 'Escherichia coli',
                        'search_string': 'Escherichia coli',
                    },
                    {
                        'label': 'Alphaproteobacteria',
                        'search_string': 'tax_string:"Alphaproteobacteria"',
                    },
                ],
                'rna_type': [
                    {
                        'label': 'lncRNA',
                        'search_string': 'rna_type:"lncRNA"',
                    },
                    {
                        'label': 'miRNA',
                        'search_string': 'rna_type:"miRNA"',
                    },
                    {
                        'label': 'snoRNA',
                        'search_string': 'rna_type:"snoRNA"',
                    },
                ],
                'expert_db': [
                    {
                        'label': 'miRBase',
                        'search_string': 'expert_db:"miRBase"',
                    },
                    {
                        'label': 'FlyBase',
                        'search_string': 'expert_db:"FlyBase"',
                    },
                    {
                        'label': 'GtRNAdb',
                        'search_string': 'expert_db:"GtRNAdb"',
                    },
                    {
                        'label': 'HGNC',
                        'search_string': 'expert_db:"HGNC"',
                    },
                    {
                        'label': 'LNCipedia',
                        'search_string': 'expert_db:"LNCipedia"',
                    },
                    {
                        'label': 'TarBase',
                        'search_string': 'expert_db:"TarBase"',
                    },
                  ],
                    'accession': [
                    {
                        'label': 'ENSG00000228630',
                        'search_string': 'ENSG00000228630',
                    },
                    {
                        'label': '4V4Q',
                        'search_string': '4V4Q',
                    },
                    {
                        'label': 'hsa-mir-126',
                        'search_string': '"hsa-mir-126"',
                    },
                    {
                        'label': "PMID:27174936",
                        'search_string': 'pubmed:"27174936"',
                    },
                    {
                        'label': 'GO:0043410',
                        'search_string': '"GO:2000352"',
                    },
                  ]
            }
            randomExamples = new Array();
            Object.keys(examples).forEach(function(example_type) {
                randomExamples.push(examples[example_type][Math.floor(Math.random()*examples[example_type].length)]);
            });
            return randomExamples;
        }
    }]
};

angular.module('textSearch').component('textSearchBar', textSearchBar);
<|endoftext|>


<commit_before>"use strict";

module.exports = {
    productionPre: [
        "build"
    ],
    productionPost: [
        "build/assets/scss/**",
        "build/assets/js/foundation.min.js",
        "build/assets/js/main.js",
        "build/assets/js/site/**",
        "build/assets/pug-*/**",
        "build/assets/fonts/*.json"
    ]
};
<commit_msg>Removed all JS files but all.js and modernizr.js for production build<commit_after>"use strict";

module.exports = {
    productionPre: [
        "build"
    ],
    productionPost: [
        "build/assets/scss/**",
        "build/assets/js/**/*.js",
        "!build/assets/js/all.js",
        "!build/assets/js/vendor/modernizr.js",
        "build/assets/js/site/",
        "build/assets/pug-*/**",
        "build/assets/fonts/*.json"
    ]
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

var ejs = require('ejs')
var fs = require('fs')
var minimatch = require('minimatch')
var mkdirp = require('mkdirp')
var path = require('path')
var program = require('commander')
var readline = require('readline')
var sortedObject = require('sorted-object')
var util = require('util')

var MODE_0666 = parseInt('0666', 8)
var MODE_0755 = parseInt('0755', 8)
var TEMPLATE_DIR = path.join(__dirname, '..', 'templates')
var VERSION = require('../package').version

var _exit = process.exit

// Re-assign process.exit because of commander
// TODO: Switch to a different command framework
process.exit = exit

// CLI

around(program, 'optionMissingArgument', function (fn, args) {
  program.outputHelp()
  fn.apply(this, args)
  return { args: [], unknown: [] }
})

before(program, 'outputHelp', function () {
  // track if help was shown for unknown option
  this._helpShown = true
})

before(program, 'unknownOption', function () {
  // allow unknown options if help was shown, to prevent trailing error
  this._allowUnknownOption = this._helpShown

  // show help if not yet shown
  if (!this._helpShown) {
    program.outputHelp()
  }
})

program
  .name('express')
  .version(VERSION, '    --version')
  .usage('[options] [dir]')
  .option('-e, --ejs', 'add ejs engine support', renamedOption('--ejs', '--view=ejs'))
  .option('    --pug', 'add pug engine support', renamedOption('--pug', '--view=pug'))
  .option('    --hbs', 'add handlebars engine support', renamedOption('--hbs', '--view=hbs'))
  .option('-H, --hogan', 'add hogan.js engine support', renamedOption('--hogan', '--view=hogan'))
  .option('-v, --view <engine>', 'add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)')
  .option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)')
  .option('    --git', 'add .gitignore')
  .option('-f, --force', 'force on non-empty directory')
  .parse(process.argv)

if (!exit.exited) {
  main()
}

/**
 * Install an around function; AOP.
 */

function around (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    var args = new Array(arguments.length)
    for (var i = 0; i < args.length; i++) args[i] = arguments[i]
    return fn.call(this, old, args)
  }
}

/**
 * Install a before function; AOP.
 */

function before (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    fn.call(this)
    old.apply(this, arguments)
  }
}

/**
 * Prompt for confirmation on STDOUT/STDIN
 */

function confirm (msg, callback) {
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question(msg, function (input) {
    rl.close()
    callback(/^y|yes|ok|true$/i.test(input))
  })
}

/**
 * Copy file from template directory.
 */

function copyTemplate (from, to) {
  write(to, fs.readFileSync(path.join(TEMPLATE_DIR, from), 'utf-8'))
}

/**
 * Copy multiple files from template directory.
 */

function copyTemplateMulti (fromDir, toDir, nameGlob) {
  fs.readdirSync(path.join(TEMPLATE_DIR, fromDir))
  .filter(minimatch.filter(nameGlob, { matchBase: true }))
  .forEach(function (name) {
    copyTemplate(path.join(fromDir, name), path.join(toDir, name))
  })
}

/**
 * Create application at the given directory.
 *
 * @param {string} name
 * @param {string} dir
 */

function createApplication (name, dir) {
  console.log()

  // Package
  var pkg = {
    name: name,
    version: '0.0.0',
    private: true,
    scripts: {
      start: 'node ./bin/www'
    },
    dependencies: {
      'debug': '~2.6.9',
      'express': '~4.16.0'
    }
  }

  // JavaScript
  var app = loadTemplate('js/app.js')
  var www = loadTemplate('js/www')

  // App name
  www.locals.name = name

  // App modules
  app.locals.modules = Object.create(null)
  app.locals.uses = []

  // Request logger
  app.locals.modules.logger = 'morgan'
  app.locals.uses.push("logger('dev')")
  pkg.dependencies.morgan = '~1.9.0'

  // Body parsers
  app.locals.uses.push('express.json()')
  app.locals.uses.push('express.urlencoded({ extended: false })')

  // Cookie parser
  app.locals.modules.cookieParser = 'cookie-parser'
  app.locals.uses.push('cookieParser()')
  pkg.dependencies['cookie-parser'] = '~1.4.3'

  if (dir !== '.') {
    mkdir(dir, '.')
  }

  mkdir(dir, 'public')
  mkdir(dir, 'public/javascripts')
  mkdir(dir, 'public/images')
  mkdir(dir, 'public/stylesheets')

  // copy css templates
  switch (program.css) {
    case 'less':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.less')
      break
    case 'stylus':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.styl')
      break
    case 'compass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.scss')
      break
    case 'sass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.sass')
      break
    default:
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.css')
      break
  }

  // copy route templates
  mkdir(dir, 'routes')
  copyTemplateMulti('js/routes', dir + '/routes', '*.js')

  // copy view templates
  mkdir(dir, 'views')
  switch (program.view) {
    case 'dust':
      copyTemplateMulti('views', dir + '/views', '*.dust')
      break
    case 'ejs':
      copyTemplateMulti('views', dir + '/views', '*.ejs')
      break
    case 'jade':
      copyTemplateMulti('views', dir + '/views', '*.jade')
      break
    case 'hjs':
      copyTemplateMulti('views', dir + '/views', '*.hjs')
      break
    case 'hbs':
      copyTemplateMulti('views', dir + '/views', '*.hbs')
      break
    case 'pug':
      copyTemplateMulti('views', dir + '/views', '*.pug')
      break
    case 'twig':
      copyTemplateMulti('views', dir + '/views', '*.twig')
      break
    case 'vash':
      copyTemplateMulti('views', dir + '/views', '*.vash')
      break
  }

  // CSS Engine support
  switch (program.css) {
    case 'compass':
      app.locals.modules.compass = 'node-compass'
      app.locals.uses.push("compass({ mode: 'expanded' })")
      pkg.dependencies['node-compass'] = '0.2.3'
      break
    case 'less':
      app.locals.modules.lessMiddleware = 'less-middleware'
      app.locals.uses.push("lessMiddleware(path.join(__dirname, 'public'))")
      pkg.dependencies['less-middleware'] = '~2.2.1'
      break
    case 'sass':
      app.locals.modules.sassMiddleware = 'node-sass-middleware'
      app.locals.uses.push("sassMiddleware({\n  src: path.join(__dirname, 'public'),\n  dest: path.join(__dirname, 'public'),\n  indentedSyntax: true, // true = .sass and false = .scss\n  sourceMap: true\n})")
      pkg.dependencies['node-sass-middleware'] = '0.11.0'
      break
    case 'stylus':
      app.locals.modules.stylus = 'stylus'
      app.locals.uses.push("stylus.middleware(path.join(__dirname, 'public'))")
      pkg.dependencies['stylus'] = '0.54.5'
      break
  }

  // Template support
  switch (program.view) {
    case 'dust':
      app.locals.modules.adaro = 'adaro'
      app.locals.view = {
        engine: 'dust',
        render: 'adaro.dust()'
      }
      break
    default:
      app.locals.view = {
        engine: program.view
      }
      break
  }

  // Static files
  app.locals.uses.push("express.static(path.join(__dirname, 'public'))")

  switch (program.view) {
    case 'dust':
      pkg.dependencies.adaro = '~1.0.4'
      break
    case 'jade':
      pkg.dependencies['jade'] = '~1.11.0'
      break
    case 'ejs':
      pkg.dependencies['ejs'] = '~2.5.7'
      break
    case 'hjs':
      pkg.dependencies['hjs'] = '~0.0.6'
      break
    case 'hbs':
      pkg.dependencies['hbs'] = '~4.0.1'
      break
    case 'pug':
      pkg.dependencies['pug'] = '2.0.0-beta11'
      break
    case 'twig':
      pkg.dependencies['twig'] = '~0.10.3'
      break
    case 'vash':
      pkg.dependencies['vash'] = '~0.12.4'
      break
  }

  if (program.git) {
    copyTemplate('js/gitignore', path.join(dir, '.gitignore'))
  }

  // sort dependencies like npm(1)
  pkg.dependencies = sortedObject(pkg.dependencies)

  // write files
  write(path.join(dir, 'app.js'), app.render())
  write(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n')
  mkdir(dir, 'bin')
  write(path.join(dir, 'bin/www'), www.render(), MODE_0755)

  var prompt = launchedFromCmd() ? '>' : '$'

  if (dir !== '.') {
    console.log()
    console.log('   change directory:')
    console.log('     %s cd %s', prompt, dir)
  }

  console.log()
  console.log('   install dependencies:')
  console.log('     %s npm install', prompt)
  console.log()
  console.log('   run the app:')

  if (launchedFromCmd()) {
    console.log('     %s SET DEBUG=%s:* & npm start', prompt, name)
  } else {
    console.log('     %s DEBUG=%s:* npm start', prompt, name)
  }

  console.log()
}

/**
 * Create an app name from a directory path, fitting npm naming requirements.
 *
 * @param {String} pathName
 */

function createAppName (pathName) {
  return path.basename(pathName)
    .replace(/[^A-Za-z0-9.-]+/g, '-')
    .replace(/^[-_.]+|-+$/g, '')
    .toLowerCase()
}

/**
 * Check if the given directory `dir` is empty.
 *
 * @param {String} dir
 * @param {Function} fn
 */

function emptyDirectory (dir, fn) {
  fs.readdir(dir, function (err, files) {
    if (err && err.code !== 'ENOENT') throw err
    fn(!files || !files.length)
  })
}

/**
 * Graceful exit for async STDIO
 */

function exit (code) {
  // flush output for Node.js Windows pipe bug
  // https://github.com/joyent/node/issues/6247 is just one bug example
  // https://github.com/visionmedia/mocha/issues/333 has a good discussion
  function done () {
    if (!(draining--)) _exit(code)
  }

  var draining = 0
  var streams = [process.stdout, process.stderr]

  exit.exited = true

  streams.forEach(function (stream) {
    // submit empty write request and wait for completion
    draining += 1
    stream.write('', done)
  })

  done()
}

/**
 * Determine if launched from cmd.exe
 */

function launchedFromCmd () {
  return process.platform === 'win32' &&
    process.env._ === undefined
}

/**
 * Load template file.
 */

function loadTemplate (name) {
  var contents = fs.readFileSync(path.join(__dirname, '..', 'templates', (name + '.ejs')), 'utf-8')
  var locals = Object.create(null)

  function render () {
    return ejs.render(contents, locals)
  }

  return {
    locals: locals,
    render: render
  }
}

/**
 * Main program.
 */

function main () {
  // Path
  var destinationPath = program.args.shift() || '.'

  // App name
  var appName = createAppName(path.resolve(destinationPath)) || 'hello-world'

  // View engine
  if (program.view === undefined) {
    if (program.ejs) program.view = 'ejs'
    if (program.hbs) program.view = 'hbs'
    if (program.hogan) program.view = 'hjs'
    if (program.pug) program.view = 'pug'
  }

  // Default view engine
  if (program.view === undefined) {
    warning('the default view engine will not be jade in future releases\n' +
      "use `--view=jade' or `--help' for additional options")
    program.view = 'jade'
  }

  // Generate application
  emptyDirectory(destinationPath, function (empty) {
    if (empty || program.force) {
      createApplication(appName, destinationPath)
    } else {
      confirm('destination is not empty, continue? [y/N] ', function (ok) {
        if (ok) {
          process.stdin.destroy()
          createApplication(appName, destinationPath)
        } else {
          console.error('aborting')
          exit(1)
        }
      })
    }
  })
}

/**
 * Make the given dir relative to base.
 *
 * @param {string} base
 * @param {string} dir
 */

function mkdir (base, dir) {
  var loc = path.join(base, dir)

  console.log('   \x1b[36mcreate\x1b[0m : ' + loc + path.sep)
  mkdirp.sync(loc, MODE_0755)
}

/**
 * Generate a callback function for commander to warn about renamed option.
 *
 * @param {String} originalName
 * @param {String} newName
 */

function renamedOption (originalName, newName) {
  return function (val) {
    warning(util.format("option `%s' has been renamed to `%s'", originalName, newName))
    return val
  }
}

/**
 * Display a warning similar to how errors are displayed by commander.
 *
 * @param {String} message
 */

function warning (message) {
  console.error()
  message.split('\n').forEach(function (line) {
    console.error('  warning: %s', line)
  })
  console.error()
}

/**
 * echo str > file.
 *
 * @param {String} file
 * @param {String} str
 */

function write (file, str, mode) {
  fs.writeFileSync(file, str, { mode: mode || MODE_0666 })
  console.log('   \x1b[36mcreate\x1b[0m : ' + file)
}
<commit_msg>lint: remove duplicate view switch block<commit_after>#!/usr/bin/env node

var ejs = require('ejs')
var fs = require('fs')
var minimatch = require('minimatch')
var mkdirp = require('mkdirp')
var path = require('path')
var program = require('commander')
var readline = require('readline')
var sortedObject = require('sorted-object')
var util = require('util')

var MODE_0666 = parseInt('0666', 8)
var MODE_0755 = parseInt('0755', 8)
var TEMPLATE_DIR = path.join(__dirname, '..', 'templates')
var VERSION = require('../package').version

var _exit = process.exit

// Re-assign process.exit because of commander
// TODO: Switch to a different command framework
process.exit = exit

// CLI

around(program, 'optionMissingArgument', function (fn, args) {
  program.outputHelp()
  fn.apply(this, args)
  return { args: [], unknown: [] }
})

before(program, 'outputHelp', function () {
  // track if help was shown for unknown option
  this._helpShown = true
})

before(program, 'unknownOption', function () {
  // allow unknown options if help was shown, to prevent trailing error
  this._allowUnknownOption = this._helpShown

  // show help if not yet shown
  if (!this._helpShown) {
    program.outputHelp()
  }
})

program
  .name('express')
  .version(VERSION, '    --version')
  .usage('[options] [dir]')
  .option('-e, --ejs', 'add ejs engine support', renamedOption('--ejs', '--view=ejs'))
  .option('    --pug', 'add pug engine support', renamedOption('--pug', '--view=pug'))
  .option('    --hbs', 'add handlebars engine support', renamedOption('--hbs', '--view=hbs'))
  .option('-H, --hogan', 'add hogan.js engine support', renamedOption('--hogan', '--view=hogan'))
  .option('-v, --view <engine>', 'add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)')
  .option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)')
  .option('    --git', 'add .gitignore')
  .option('-f, --force', 'force on non-empty directory')
  .parse(process.argv)

if (!exit.exited) {
  main()
}

/**
 * Install an around function; AOP.
 */

function around (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    var args = new Array(arguments.length)
    for (var i = 0; i < args.length; i++) args[i] = arguments[i]
    return fn.call(this, old, args)
  }
}

/**
 * Install a before function; AOP.
 */

function before (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    fn.call(this)
    old.apply(this, arguments)
  }
}

/**
 * Prompt for confirmation on STDOUT/STDIN
 */

function confirm (msg, callback) {
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question(msg, function (input) {
    rl.close()
    callback(/^y|yes|ok|true$/i.test(input))
  })
}

/**
 * Copy file from template directory.
 */

function copyTemplate (from, to) {
  write(to, fs.readFileSync(path.join(TEMPLATE_DIR, from), 'utf-8'))
}

/**
 * Copy multiple files from template directory.
 */

function copyTemplateMulti (fromDir, toDir, nameGlob) {
  fs.readdirSync(path.join(TEMPLATE_DIR, fromDir))
  .filter(minimatch.filter(nameGlob, { matchBase: true }))
  .forEach(function (name) {
    copyTemplate(path.join(fromDir, name), path.join(toDir, name))
  })
}

/**
 * Create application at the given directory.
 *
 * @param {string} name
 * @param {string} dir
 */

function createApplication (name, dir) {
  console.log()

  // Package
  var pkg = {
    name: name,
    version: '0.0.0',
    private: true,
    scripts: {
      start: 'node ./bin/www'
    },
    dependencies: {
      'debug': '~2.6.9',
      'express': '~4.16.0'
    }
  }

  // JavaScript
  var app = loadTemplate('js/app.js')
  var www = loadTemplate('js/www')

  // App name
  www.locals.name = name

  // App modules
  app.locals.modules = Object.create(null)
  app.locals.uses = []

  // Request logger
  app.locals.modules.logger = 'morgan'
  app.locals.uses.push("logger('dev')")
  pkg.dependencies.morgan = '~1.9.0'

  // Body parsers
  app.locals.uses.push('express.json()')
  app.locals.uses.push('express.urlencoded({ extended: false })')

  // Cookie parser
  app.locals.modules.cookieParser = 'cookie-parser'
  app.locals.uses.push('cookieParser()')
  pkg.dependencies['cookie-parser'] = '~1.4.3'

  if (dir !== '.') {
    mkdir(dir, '.')
  }

  mkdir(dir, 'public')
  mkdir(dir, 'public/javascripts')
  mkdir(dir, 'public/images')
  mkdir(dir, 'public/stylesheets')

  // copy css templates
  switch (program.css) {
    case 'less':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.less')
      break
    case 'stylus':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.styl')
      break
    case 'compass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.scss')
      break
    case 'sass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.sass')
      break
    default:
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.css')
      break
  }

  // copy route templates
  mkdir(dir, 'routes')
  copyTemplateMulti('js/routes', dir + '/routes', '*.js')

  // copy view templates
  mkdir(dir, 'views')
  switch (program.view) {
    case 'dust':
      copyTemplateMulti('views', dir + '/views', '*.dust')
      break
    case 'ejs':
      copyTemplateMulti('views', dir + '/views', '*.ejs')
      break
    case 'jade':
      copyTemplateMulti('views', dir + '/views', '*.jade')
      break
    case 'hjs':
      copyTemplateMulti('views', dir + '/views', '*.hjs')
      break
    case 'hbs':
      copyTemplateMulti('views', dir + '/views', '*.hbs')
      break
    case 'pug':
      copyTemplateMulti('views', dir + '/views', '*.pug')
      break
    case 'twig':
      copyTemplateMulti('views', dir + '/views', '*.twig')
      break
    case 'vash':
      copyTemplateMulti('views', dir + '/views', '*.vash')
      break
  }

  // CSS Engine support
  switch (program.css) {
    case 'compass':
      app.locals.modules.compass = 'node-compass'
      app.locals.uses.push("compass({ mode: 'expanded' })")
      pkg.dependencies['node-compass'] = '0.2.3'
      break
    case 'less':
      app.locals.modules.lessMiddleware = 'less-middleware'
      app.locals.uses.push("lessMiddleware(path.join(__dirname, 'public'))")
      pkg.dependencies['less-middleware'] = '~2.2.1'
      break
    case 'sass':
      app.locals.modules.sassMiddleware = 'node-sass-middleware'
      app.locals.uses.push("sassMiddleware({\n  src: path.join(__dirname, 'public'),\n  dest: path.join(__dirname, 'public'),\n  indentedSyntax: true, // true = .sass and false = .scss\n  sourceMap: true\n})")
      pkg.dependencies['node-sass-middleware'] = '0.11.0'
      break
    case 'stylus':
      app.locals.modules.stylus = 'stylus'
      app.locals.uses.push("stylus.middleware(path.join(__dirname, 'public'))")
      pkg.dependencies['stylus'] = '0.54.5'
      break
  }

  // Template support
  switch (program.view) {
    case 'dust':
      app.locals.modules.adaro = 'adaro'
      app.locals.view = {
        engine: 'dust',
        render: 'adaro.dust()'
      }
      pkg.dependencies.adaro = '~1.0.4'
      break
    case 'ejs':
      app.locals.view = { engine: 'ejs' }
      pkg.dependencies.ejs = '~2.5.7'
      break
    case 'hbs':
      app.locals.view = { engine: 'hbs' }
      pkg.dependencies.hbs = '~4.0.1'
      break
    case 'hjs':
      app.locals.view = { engine: 'hjs' }
      pkg.dependencies.hjs = '~0.0.6'
      break
    case 'jade':
      app.locals.view = { engine: 'jade' }
      pkg.dependencies.jade = '~1.11.0'
      break
    case 'pug':
      app.locals.view = { engine: 'pug' }
      pkg.dependencies.pug = '2.0.0-beta11'
      break
    case 'twig':
      app.locals.view = { engine: 'twig' }
      pkg.dependencies.twig = '~0.10.3'
      break
    case 'vash':
      app.locals.view = { engine: 'vash' }
      pkg.dependencies.vash = '~0.12.4'
      break
  }

  // Static files
  app.locals.uses.push("express.static(path.join(__dirname, 'public'))")

  if (program.git) {
    copyTemplate('js/gitignore', path.join(dir, '.gitignore'))
  }

  // sort dependencies like npm(1)
  pkg.dependencies = sortedObject(pkg.dependencies)

  // write files
  write(path.join(dir, 'app.js'), app.render())
  write(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n')
  mkdir(dir, 'bin')
  write(path.join(dir, 'bin/www'), www.render(), MODE_0755)

  var prompt = launchedFromCmd() ? '>' : '$'

  if (dir !== '.') {
    console.log()
    console.log('   change directory:')
    console.log('     %s cd %s', prompt, dir)
  }

  console.log()
  console.log('   install dependencies:')
  console.log('     %s npm install', prompt)
  console.log()
  console.log('   run the app:')

  if (launchedFromCmd()) {
    console.log('     %s SET DEBUG=%s:* & npm start', prompt, name)
  } else {
    console.log('     %s DEBUG=%s:* npm start', prompt, name)
  }

  console.log()
}

/**
 * Create an app name from a directory path, fitting npm naming requirements.
 *
 * @param {String} pathName
 */

function createAppName (pathName) {
  return path.basename(pathName)
    .replace(/[^A-Za-z0-9.-]+/g, '-')
    .replace(/^[-_.]+|-+$/g, '')
    .toLowerCase()
}

/**
 * Check if the given directory `dir` is empty.
 *
 * @param {String} dir
 * @param {Function} fn
 */

function emptyDirectory (dir, fn) {
  fs.readdir(dir, function (err, files) {
    if (err && err.code !== 'ENOENT') throw err
    fn(!files || !files.length)
  })
}

/**
 * Graceful exit for async STDIO
 */

function exit (code) {
  // flush output for Node.js Windows pipe bug
  // https://github.com/joyent/node/issues/6247 is just one bug example
  // https://github.com/visionmedia/mocha/issues/333 has a good discussion
  function done () {
    if (!(draining--)) _exit(code)
  }

  var draining = 0
  var streams = [process.stdout, process.stderr]

  exit.exited = true

  streams.forEach(function (stream) {
    // submit empty write request and wait for completion
    draining += 1
    stream.write('', done)
  })

  done()
}

/**
 * Determine if launched from cmd.exe
 */

function launchedFromCmd () {
  return process.platform === 'win32' &&
    process.env._ === undefined
}

/**
 * Load template file.
 */

function loadTemplate (name) {
  var contents = fs.readFileSync(path.join(__dirname, '..', 'templates', (name + '.ejs')), 'utf-8')
  var locals = Object.create(null)

  function render () {
    return ejs.render(contents, locals)
  }

  return {
    locals: locals,
    render: render
  }
}

/**
 * Main program.
 */

function main () {
  // Path
  var destinationPath = program.args.shift() || '.'

  // App name
  var appName = createAppName(path.resolve(destinationPath)) || 'hello-world'

  // View engine
  if (program.view === undefined) {
    if (program.ejs) program.view = 'ejs'
    if (program.hbs) program.view = 'hbs'
    if (program.hogan) program.view = 'hjs'
    if (program.pug) program.view = 'pug'
  }

  // Default view engine
  if (program.view === undefined) {
    warning('the default view engine will not be jade in future releases\n' +
      "use `--view=jade' or `--help' for additional options")
    program.view = 'jade'
  }

  // Generate application
  emptyDirectory(destinationPath, function (empty) {
    if (empty || program.force) {
      createApplication(appName, destinationPath)
    } else {
      confirm('destination is not empty, continue? [y/N] ', function (ok) {
        if (ok) {
          process.stdin.destroy()
          createApplication(appName, destinationPath)
        } else {
          console.error('aborting')
          exit(1)
        }
      })
    }
  })
}

/**
 * Make the given dir relative to base.
 *
 * @param {string} base
 * @param {string} dir
 */

function mkdir (base, dir) {
  var loc = path.join(base, dir)

  console.log('   \x1b[36mcreate\x1b[0m : ' + loc + path.sep)
  mkdirp.sync(loc, MODE_0755)
}

/**
 * Generate a callback function for commander to warn about renamed option.
 *
 * @param {String} originalName
 * @param {String} newName
 */

function renamedOption (originalName, newName) {
  return function (val) {
    warning(util.format("option `%s' has been renamed to `%s'", originalName, newName))
    return val
  }
}

/**
 * Display a warning similar to how errors are displayed by commander.
 *
 * @param {String} message
 */

function warning (message) {
  console.error()
  message.split('\n').forEach(function (line) {
    console.error('  warning: %s', line)
  })
  console.error()
}

/**
 * echo str > file.
 *
 * @param {String} file
 * @param {String} str
 */

function write (file, str, mode) {
  fs.writeFileSync(file, str, { mode: mode || MODE_0666 })
  console.log('   \x1b[36mcreate\x1b[0m : ' + file)
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name          amazon_wishlist_sum
// @namespace     danibaena
// @description   Just a simple script to sum all items and prices in an Amazon.es' Wishlist url
// @include       http://www.amazon.es/gp/registry/wishlist/*
// @include       https://www.amazon.es/gp/registry/wishlist/*
// @include       https://www.amazon.es/hz/wishlist/ls/*
// @include       http://www.amazon.es/hz/wishlist/ls/*
// @include       http://www.amazon.es/gp/aw/ls/*
// @include       https://www.amazon.es/gp/aw/ls/*
// @require       https://code.jquery.com/jquery-3.2.1.slim.min.js
// @require       https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant         none
// ==/UserScript==

function addItemPricesSum() {
    var itemsList               = $(".g-item-sortable[data-id]").length ? $(".g-item-sortable[data-id]") : $(".awl-item-wrapper[data-price]") ;
    var priceClass              = "a-price-whole";
    var paginationID            = "wishlistPagination";
    var pages                   = document.getElementById(paginationID);
    var page                    = pages ? "Datos para esta página de la lista:
" : "Datos de la lista:
";
    var itemSum                 = 0;
    var itemCounter             = 0;
    var itemNotAvailableCounter = 0;

    itemsList.each(function(index , item) {
        var result = $(item).attr('data-price');
        result     = result !== '-Infinity' ? parseFloat(result) : result;
        if(result === '-Infinity') {
            itemNotAvailableCounter++;
        } else {
            itemSum += result;
            itemCounter++;
        }
    });

    itemSum      = itemSum.toFixed(2);
    itemSumCoinc = (itemSum/1.04).toFixed(2);
    itemSumCoinc = itemSumCoinc.toString().replace(".",",").replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");
    itemSum      = itemSum.toString().replace(".",",").replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");

    var textList = `

${page}

            Artículos totales:
            ${itemCounter}

            Artículos no disponibles:
            ${itemNotAvailableCounter}

            Coste total:
            ${itemSum} €

            Coste total con COINC:
            ${itemSumCoinc} €

    `;

    var ruleDivider = '
';
    ruleDivider     = isMobile() ? ruleDivider : '';

    var scriptList = `
        ${ruleDivider}

            ${textList}

    `;

    var $scriptList = $('#scriptlist');

    if($scriptList.length) {
        $scriptList.replaceWith(scriptList);
    } else {
        $(scriptList).insertAfter('#endOfListMarker');
    }
}

var isMobile = function() {
    return /Mobi/i.test(navigator.userAgent) || /Android/i.test(navigator.userAgent);
};

var scriptListStyles = `

`;

$(scriptListStyles).appendTo('head');

var elementToWait = $(".g-item-sortable[data-id]").length ? ".g-item-sortable[data-id]" : ".awl-item-wrapper[data-price]";
waitForKeyElements (elementToWait, addItemPricesSum, false);
<commit_msg>Fix block not being rendered in default wishlist<commit_after>// ==UserScript==
// @name          amazon_wishlist_sum
// @namespace     danibaena
// @description   Just a simple script to sum all items and prices in an Amazon.es' Wishlist url
// @include       http://www.amazon.es/gp/registry/wishlist/*
// @include       https://www.amazon.es/gp/registry/wishlist/*
// @include       https://www.amazon.es/hz/wishlist/ls/*
// @include       http://www.amazon.es/hz/wishlist/ls/*
// @include       http://www.amazon.es/gp/aw/ls/*
// @include       https://www.amazon.es/gp/aw/ls/*
// @require       https://code.jquery.com/jquery-3.2.1.slim.min.js
// @require       https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant         none
// ==/UserScript==

function addItemPricesSum() {
    var itemsList               = !isMobile() ? $(".g-item-sortable[data-id]") : $(".awl-item-wrapper[data-price]") ;
    var priceClass              = "a-price-whole";
    var paginationID            = "wishlistPagination";
    var pages                   = document.getElementById(paginationID);
    var page                    = pages ? "Datos para esta página de la lista:
" : "Datos de la lista:
";
    var itemSum                 = 0;
    var itemCounter             = 0;
    var itemNotAvailableCounter = 0;

    itemsList.each(function(index , item) {
        var result = $(item).attr('data-price');
        result     = result !== '-Infinity' ? parseFloat(result) : result;
        if(result === '-Infinity') {
            itemNotAvailableCounter++;
        } else {
            itemSum += result;
            itemCounter++;
        }
    });

    itemSum      = itemSum.toFixed(2);
    itemSumCoinc = (itemSum/1.04).toFixed(2);
    itemSumCoinc = itemSumCoinc.toString().replace(".",",").replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");
    itemSum      = itemSum.toString().replace(".",",").replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1.");

    var textList = `

${page}

            Artículos totales:
            ${itemCounter}

            Artículos no disponibles:
            ${itemNotAvailableCounter}

            Coste total:
            ${itemSum} €

            Coste total con COINC:
            ${itemSumCoinc} €

    `;

    var ruleDivider = '
';

    var scriptList = `
        ${isMobile() ? ruleDivider : ''}

            ${textList}

    `;

    var $scriptList = $('#scriptlist');

    if($scriptList.length) {
        $scriptList.replaceWith(scriptList);
    } else {
        $(scriptList).insertAfter('#endOfListMarker');
    }
}

var isMobile = function() {
    return /Mobi/i.test(navigator.userAgent) || /Android/i.test(navigator.userAgent);
};

var scriptListStyles = `

`;

$(scriptListStyles).appendTo('head');

var elementToWait = !isMobile() ? ".g-item-sortable[data-id]" : ".awl-item-wrapper[data-price]";
waitForKeyElements (elementToWait, addItemPricesSum, false);
waitForKeyElements ('#endOfListMarker', addItemPricesSum, false);
<|endoftext|>


<commit_before>const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

exports.createPlayer = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/players')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL,
      rating: 750,
      rated: false
    })
})
exports.createMatch = functions.database.ref('/users/{userUid}/open-matches/{matchUid}')
  .onWrite(event => {
    if (event.auth.admin || event.data.previous.exists() || !event.data.exists()) { return }

    let match = event.data.val()
    if (match.player1Uid === match.player2Uid) { return }

    match.createdDate = new Date().toISOString()
    let details = {
      player1Uid: match.player1Uid,
      player2Uid: match.player2Uid,
      createdDate: match.createdDate,
      approvals: {player1: '', player2: ''},
      scores: [{player1: 0, player2: 0}]
    }

    return Promise.all([
      admin.database()
        .ref(`/users/${match.player1Uid}/open-matches`)
        .child(event.params.matchUid)
        .set(match),
      admin.database()
        .ref(`/users/${match.player2Uid}/open-matches`)
        .child(event.params.matchUid)
        .set(match),
      admin.database()
        .ref(`/open-match-details/${event.params.matchUid}`)
        .set(details)
    ])
  })
exports.rejectMatch = functions.database.ref('/open-match-details/{matchUid}/removed')
  .onWrite(event => {
    if (event.auth.admin || !event.data.exists() || !event.data.val()) { return }

    return admin.database().ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        return Promise.all([
          admin.database()
            .ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`)
            .set(null),
          admin.database()
            .ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`)
            .set(null),
          admin.database()
            .ref(`/open-match-details/${event.params.matchUid}`)
            .set(null)
        ])
      })
  })
exports.approvalsChanged = functions.database.ref('/open-match-details/{matchUid}/approvals')
  .onWrite((event) => {
    if (event.auth.admin || !event.data.exists() || !event.data.previous.exists()) { return }

    let approvals = event.data.val()
    if (!approvals.player1 || !approvals.player2) { return }

    return admin.database().ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        let closingApproval = details.scores.reduce((approval, score) => {
          return approval + `|${score.player1}|${score.player2}`
        }, '')
        if (closingApproval !== details.approvals.player1 ||
          closingApproval !== details.approvals.player2) {
          return
        }

        let player1Games = details.scores.filter((s) => Number(s.player1) > Number(s.player2)).length
        let player2Games = details.scores.length - player1Games

        let finalMatch = {
          finalizedDate: new Date().toISOString(),
          createdDate: details.createdDate,
          player1Uid: details.player1Uid,
          player2Uid: details.player2Uid,
          scores: details.scores,
          player1Games: player1Games,
          player2Games: player2Games,
          winnerUid: player1Games > player2Games ? details.player1Uid : details.player2Uid
        }
        return Promise.all([
          admin.database().ref(`/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player1Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player2Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`).set(null),
          admin.database().ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`).set(null),
          admin.database().ref(`/open-match-details/${event.params.matchUid}`).set(null)
        ])
      })
  })

exports.updateRating = functions.database.ref('/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.previous.exists() || !event.data.exists()) { return }

    let match = event.data.val()
    return Promise.all([
      admin.database().ref(`/players/${match.player1Uid}`).once('value'),
      admin.database().ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs) => {
      let p1Rating = playerRefs[0].val().rating
      let p2Rating = playerRefs[1].val().rating
      let p1Score = match.scores.reduce((total, i) => total + Number(i.player1), 0)
      let p2Score = match.scores.reduce((total, i) => total + Number(i.player2), 0)
      let p1Prob = calcProbability(p1Rating, p2Rating)
      let p2Prob = calcProbability(p2Rating, p1Rating)
      let kFactor = 50 * calcKFactorMultiplier(p1Score, p2Score, p1Rating, p2Rating)
      let p1Result = match.player1Games > match.player2Games ? 1 : match.player1Games < match.player2Games ? 0 : 0.5
      let p2Result = 1 - p1Result
      let player1NewRating = calcNewRating(p1Prob, p1Result, p1Rating, kFactor)
      let player2NewRating = calcNewRating(p2Prob, p2Result, p2Rating, kFactor)
      let ratings = {
        player1Prev: p1Rating,
        player2Prev: p2Rating,
        player1New: player1NewRating,
        player2New: player2NewRating
      }

      return Promise.all([
        admin.database().ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        admin.database().ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating),
        admin.database().ref(`/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/players/${match.player1Uid}/rated`)
          .set(true),
        admin.database().ref(`/players/${match.player2Uid}/rated`)
          .set(true)
      ])
    })
  })

let calcKFactorMultiplier = (score1, score2, rating1, rating2) => {
  let diff = Math.log(Math.abs(score1 - score2) + 1)
  let ratingDiff = score1 > score2 ? rating1 - rating2 : rating2 - rating1
  return diff * (2.2 / (ratingDiff * 0.001 + 2.2))
}
let calcNewRating = (expected, actual, current, kFactor) => {
  return Math.round(current + kFactor * (actual - expected))
}
let calcProbability = (rating1, rating2) => {
  return 1 / (1 + Math.pow(10, ((rating2 - rating1) / 400)))
}
<commit_msg>Working with create-match - draft<commit_after>const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

exports.createPlayer = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/players')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL,
      rating: 750,
      rated: false
    })
})
exports.createMatch = functions.database.ref('/users/{userUid}/create-match/{matchUid}')
  .onWrite(event => {
    if (event.auth.admin || event.data.previous.exists() || !event.data.exists()) { return }

    let match = {
      player1Uid: event.params.userUid,
      createdDate: new Date().toISOString()
    }
    let details = Object.assign({
      approvals: {player1: '', player2: ''},
      scores: [{player1: 0, player2: 0}]
    }, match)

    return Promise.all([
      admin.database()
        .ref(`/users/${match.player1Uid}/open-matches/${event.params.matchUid}`)
        .set(match)
      // admin.database()
      //   .ref(`/users/${match.player2Uid}/open-matches/${event.params.matchUid}`)
      //   .set(match),
      // admin.database()
      //   .ref(`/open-match-details/${event.params.matchUid}`)
      //   .set(details)
    ])
  })
exports.rejectMatch = functions.database.ref('/open-match-details/{matchUid}/removed')
  .onWrite(event => {
    if (event.auth.admin || !event.data.exists() || !event.data.val()) { return }

    return admin.database().ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        return Promise.all([
          admin.database()
            .ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`)
            .set(null),
          admin.database()
            .ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`)
            .set(null),
          admin.database()
            .ref(`/open-match-details/${event.params.matchUid}`)
            .set(null)
        ])
      })
  })
exports.approvalsChanged = functions.database.ref('/open-match-details/{matchUid}/approvals')
  .onWrite((event) => {
    if (event.auth.admin || !event.data.exists() || !event.data.previous.exists()) { return }

    let approvals = event.data.val()
    if (!approvals.player1 || !approvals.player2) { return }

    return admin.database().ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        let closingApproval = details.scores.reduce((approval, score) => {
          return approval + `|${score.player1}|${score.player2}`
        }, '')
        if (closingApproval !== details.approvals.player1 ||
          closingApproval !== details.approvals.player2) {
          return
        }

        let player1Games = details.scores.filter((s) => Number(s.player1) > Number(s.player2)).length
        let player2Games = details.scores.length - player1Games

        let finalMatch = {
          finalizedDate: new Date().toISOString(),
          createdDate: details.createdDate,
          player1Uid: details.player1Uid,
          player2Uid: details.player2Uid,
          scores: details.scores,
          player1Games: player1Games,
          player2Games: player2Games,
          winnerUid: player1Games > player2Games ? details.player1Uid : details.player2Uid
        }
        return Promise.all([
          admin.database().ref(`/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player1Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player2Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          admin.database().ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`).set(null),
          admin.database().ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`).set(null),
          admin.database().ref(`/open-match-details/${event.params.matchUid}`).set(null)
        ])
      })
  })

exports.updateRating = functions.database.ref('/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.previous.exists() || !event.data.exists()) { return }

    let match = event.data.val()
    return Promise.all([
      admin.database().ref(`/players/${match.player1Uid}`).once('value'),
      admin.database().ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs) => {
      let p1Rating = playerRefs[0].val().rating
      let p2Rating = playerRefs[1].val().rating
      let p1Score = match.scores.reduce((total, i) => total + Number(i.player1), 0)
      let p2Score = match.scores.reduce((total, i) => total + Number(i.player2), 0)
      let p1Prob = calcProbability(p1Rating, p2Rating)
      let p2Prob = calcProbability(p2Rating, p1Rating)
      let kFactor = 50 * calcKFactorMultiplier(p1Score, p2Score, p1Rating, p2Rating)
      let p1Result = match.player1Games > match.player2Games ? 1 : match.player1Games < match.player2Games ? 0 : 0.5
      let p2Result = 1 - p1Result
      let player1NewRating = calcNewRating(p1Prob, p1Result, p1Rating, kFactor)
      let player2NewRating = calcNewRating(p2Prob, p2Result, p2Rating, kFactor)
      let ratings = {
        player1Prev: p1Rating,
        player2Prev: p2Rating,
        player1New: player1NewRating,
        player2New: player2NewRating
      }

      return Promise.all([
        admin.database().ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        admin.database().ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating),
        admin.database().ref(`/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        admin.database().ref(`/players/${match.player1Uid}/rated`)
          .set(true),
        admin.database().ref(`/players/${match.player2Uid}/rated`)
          .set(true)
      ])
    })
  })

let calcKFactorMultiplier = (score1, score2, rating1, rating2) => {
  let diff = Math.log(Math.abs(score1 - score2) + 1)
  let ratingDiff = score1 > score2 ? rating1 - rating2 : rating2 - rating1
  return diff * (2.2 / (ratingDiff * 0.001 + 2.2))
}
let calcNewRating = (expected, actual, current, kFactor) => {
  return Math.round(current + kFactor * (actual - expected))
}
let calcProbability = (rating1, rating2) => {
  return 1 / (1 + Math.pow(10, ((rating2 - rating1) / 400)))
}
<|endoftext|>


<commit_before>const Discord = require('discord.js')
const config = require('../../config.json')
const commandList = require('../../util/commandList.json')
const channelTracker = require('../../util/channelTracker.js')
const storage = require('../../util/storage.js')
const currentGuilds = storage.currentGuilds
const overriddenGuilds = storage.overriddenGuilds
const failedLinks = storage.failedLinks
const pageControls = require('../../util/pageControls.js')
const MsgHandler = require('../../util/MsgHandler.js')

function getFeedStatus (link, failLimit) {
  const failCount = failedLinks[link]
  if ((!failCount || typeof failCount === 'number') && (failCount < failLimit)) return `Status: OK ${failCount > Math.ceil(failLimit / 10) ? '(' + failCount + '/' + failLimit + ')' : ''}\n`
  else return `Status: FAILED\n`
}

module.exports = function (bot, message, command, callback, miscOption, firstMsgHandler) { // miscOption is for rssoptions command, firstMsgHandler holds messages that happened before this
  const guildRss = currentGuilds.get(message.guild.id)
  if (!guildRss || !guildRss.sources || guildRss.sources.size() === 0) return message.channel.send('There are no existing feeds.').catch(err => console.log(`Promise Warning: chooseFeed 2: ${err}`))

  const rssList = guildRss.sources
  const maxFeedsAllowed = overriddenGuilds[message.guild.id] != null ? overriddenGuilds[message.guild.id] === 0 ? 'Unlimited' : overriddenGuilds[message.guild.id] : (!config.feedSettings.maxFeeds || isNaN(parseInt(config.feedSettings.maxFeeds, 10))) ? 'Unlimited' : config.feedSettings.maxFeeds

  let embedMsg = new Discord.RichEmbed()
    .setColor(config.botSettings.menuColor)
    .setAuthor('Feed Selection Menu')
    .setDescription(`**Server Limit:** ${rssList.size()}/${maxFeedsAllowed}\n**Channel:** #${message.channel.name}\n**Action**: ${command === 'rssoptions' ? commandList[command].options[miscOption] : commandList[command].action}\n\nChoose a feed to from this channel by typing the number to execute your requested action on. ${commandList[command].action === 'Feed Removal' ? 'You may select multiple feeds to remove by separation with commas. ' : ''}Type **exit** to cancel.\u200b\n\u200b\n`)

  const failLimit = (config.feedSettings.failLimit && !isNaN(parseInt(config.feedSettings.failLimit, 10))) ? parseInt(config.feedSettings.failLimit, 10) : 0
  const currentRSSList = []

  for (var rssName in rssList) { // Generate the info for each feed as an object, and push into array to be used in pages that are sent
    let o = {link: rssList[rssName].link, rssName: rssName, title: rssList[rssName].title}
    if (commandList[command].action === 'Refresh Feed') o.status = getFeedStatus(rssList[rssName].link, failLimit)
    if (miscOption === 'titleChecks') o.titleChecks = rssList[rssName].checkTitles === true ? 'Title Checks: Enabled\n' : 'Title Checks: Disabled\n'
    else if (miscOption === 'disableImgLinkPreviews') o.disableImgLinkPreviews = rssList[rssName].disableImgLinkPreviews === true ? 'Image Link Previews: Disabled\n' : 'Image Link Previews: Enabled\n'
    else if (miscOption === 'disableImgLinks') o.disableImgLinks = rssList[rssName].disableImgLinks === true ? 'Image Links Existence: Disabled\n' : 'Image Link Existence: Enabled\n'

    if (message.channel.id === rssList[rssName].channel) currentRSSList.push(o)
  }

  if (currentRSSList.length === 0) return message.channel.send('No feeds assigned to this channel.').catch(err => console.log(`Promise Warning: chooseFeed 1: ${err}`))

  const msgHandler = new MsgHandler(bot, message) // For deletion at the end of a series of menus
  if (firstMsgHandler) msgHandler.merge(firstMsgHandler)

  const pages = []
  for (var x in currentRSSList) {
    const count = parseInt(x, 10) + 1
    const link = currentRSSList[x].link
    const title = currentRSSList[x].title
    const status = currentRSSList[x].status || ''
    const miscOption = currentRSSList[x].titleChecks || currentRSSList[x].disableImgLinkPreviews || currentRSSList[x].disableImgLinks || ''

    // 7 feeds per embed (AKA page)
    if ((count - 1) !== 0 && (count - 1) / 7 % 1 === 0) {
      pages.push(embedMsg)
      embedMsg = new Discord.RichEmbed().setColor(config.botSettings.menuColor).setDescription(`Page ${pages.length + 1}`)
    }

    embedMsg.addField(`${count})  ${title}`, `${miscOption}${status}Link: ${link}`)
  }

  // Push the leftover results into the last embed
  pages.push(embedMsg)

  message.channel.send({embed: pages[0]})
  .then(m => {
    msgHandler.add(m)
    selectFeed()
    if (pages.length === 1) return
    m.react('◀').then(rct => {
      m.react('▶').then(rct2 => {
        pageControls.add(m.id, pages)
      }).catch(err => console.log(`Reaction Error: (${message.guild.id}, ${message.guild.name}) => Could not add emoticon >. Reason: `, err))
    }).catch(err => console.log(`Reaction Error: (${message.guild.id}, ${message.guild.name}) => Could not add emoticon <. Reason: `, err))
  }).catch(err => console.log(`Message Error: (${message.guild.id}, ${message.guild.name}) => Could not send message of embed feed selection list. Reason: `, err))

  // Only start message collector if all pages were sent
  function selectFeed () {
    const filter = m => m.author.id === message.author.id
    const collector = message.channel.createMessageCollector(filter, {time: 60000})
    channelTracker.add(message.channel.id)

    collector.on('collect', function (m) {
      msgHandler.add(m)
      let chosenOption = m.content
      if (chosenOption.toLowerCase() === 'exit') return collector.stop('Feed selection menu closed.')

      // Return an array of selected indices for feed removal
      if (commandList[command].action === 'Feed Removal') {
        let rawArray = chosenOption.split(',')

        for (var p = rawArray.length - 1; p >= 0; p--) { // Sanitize the input
          rawArray[p] = rawArray[p].trim()
          if (!rawArray[p]) rawArray.splice(p, 1)
        }

        let chosenOptionList = rawArray.filter(function (elem, index, self) { // Remove duplicates
          return index.toString() === self.indexOf(elem).toString()
        })

        let validChosens = []
        let invalidChosens = []

        for (var z in chosenOptionList) {
          let index = parseInt(chosenOptionList[z], 10) - 1
          if (isNaN(index) || index + 1 > currentRSSList.length || index + 1 < 1) invalidChosens.push(chosenOptionList[z])
          else validChosens.push(index)
        }

        if (invalidChosens.length > 0) return message.channel.send(`The number(s) \`${invalidChosens}\` are invalid. Try again.`).then(m => msgHandler.add(m)).catch(err => console.log(`Promise Warning: chooseFeed 4: ${err}`))
        else {
          collector.stop()
          for (var q in validChosens) {
            validChosens[q] = currentRSSList[validChosens[q]].rssName
          }
          return callback(validChosens, msgHandler)
        }
      }

      // Return a single index for non feed removal actions
      const index = parseInt(chosenOption, 10) - 1

      if (isNaN(index) || index + 1 > currentRSSList.length || index + 1 < 1) return message.channel.send('That is not a valid number. Try again.').then(m => msgHandler.add(m)).catch(err => console.log(`Promise Warning: chooseFeed 4: ${err}`))

      collector.stop()
      callback(currentRSSList[index].rssName, msgHandler)
    })
    collector.on('end', function (collected, reason) {
      channelTracker.remove(message.channel.id)
      if (reason === 'user') return
      if (reason === 'time') message.channel.send(`I have closed the menu due to inactivity.`)
      else if (reason !== 'user') message.channel.send(reason).then(m => m.delete(6000))
      msgHandler.deleteAll(message.channel)
    })
  }
}
<commit_msg>Fixed all feeds showing failed on rssrefresh<commit_after>const Discord = require('discord.js')
const config = require('../../config.json')
const commandList = require('../../util/commandList.json')
const channelTracker = require('../../util/channelTracker.js')
const storage = require('../../util/storage.js')
const currentGuilds = storage.currentGuilds
const overriddenGuilds = storage.overriddenGuilds
const failedLinks = storage.failedLinks
const pageControls = require('../../util/pageControls.js')
const MsgHandler = require('../../util/MsgHandler.js')

function getFeedStatus (link, failLimit) {
  const failCount = failedLinks[link]
  console.log(failCount)
  console.log(failLimit)
  if (!failCount || (typeof failCount === 'number' && failCount <= failLimit)) return `Status: OK ${failCount > Math.ceil(failLimit / 10) ? '(' + failCount + '/' + failLimit + ')' : ''}\n`
  else return `Status: FAILED\n`
}

module.exports = function (bot, message, command, callback, miscOption, firstMsgHandler) { // miscOption is for rssoptions command, firstMsgHandler holds messages that happened before this
  const guildRss = currentGuilds.get(message.guild.id)
  if (!guildRss || !guildRss.sources || guildRss.sources.size() === 0) return message.channel.send('There are no existing feeds.').catch(err => console.log(`Promise Warning: chooseFeed 2: ${err}`))

  const rssList = guildRss.sources
  const maxFeedsAllowed = overriddenGuilds[message.guild.id] != null ? overriddenGuilds[message.guild.id] === 0 ? 'Unlimited' : overriddenGuilds[message.guild.id] : (!config.feedSettings.maxFeeds || isNaN(parseInt(config.feedSettings.maxFeeds, 10))) ? 'Unlimited' : config.feedSettings.maxFeeds

  let embedMsg = new Discord.RichEmbed()
    .setColor(config.botSettings.menuColor)
    .setAuthor('Feed Selection Menu')
    .setDescription(`**Server Limit:** ${rssList.size()}/${maxFeedsAllowed}\n**Channel:** #${message.channel.name}\n**Action**: ${command === 'rssoptions' ? commandList[command].options[miscOption] : commandList[command].action}\n\nChoose a feed to from this channel by typing the number to execute your requested action on. ${commandList[command].action === 'Feed Removal' ? 'You may select multiple feeds to remove by separation with commas. ' : ''}Type **exit** to cancel.\u200b\n\u200b\n`)

  const failLimit = (config.feedSettings.failLimit && !isNaN(parseInt(config.feedSettings.failLimit, 10))) ? parseInt(config.feedSettings.failLimit, 10) : 0
  const currentRSSList = []

  for (var rssName in rssList) { // Generate the info for each feed as an object, and push into array to be used in pages that are sent
    let o = {link: rssList[rssName].link, rssName: rssName, title: rssList[rssName].title}
    if (commandList[command].action === 'Refresh Feed') o.status = getFeedStatus(rssList[rssName].link, failLimit)
    if (miscOption === 'titleChecks') o.titleChecks = rssList[rssName].checkTitles === true ? 'Title Checks: Enabled\n' : 'Title Checks: Disabled\n'
    else if (miscOption === 'disableImgLinkPreviews') o.disableImgLinkPreviews = rssList[rssName].disableImgLinkPreviews === true ? 'Image Link Previews: Disabled\n' : 'Image Link Previews: Enabled\n'
    else if (miscOption === 'disableImgLinks') o.disableImgLinks = rssList[rssName].disableImgLinks === true ? 'Image Links Existence: Disabled\n' : 'Image Link Existence: Enabled\n'

    if (message.channel.id === rssList[rssName].channel) currentRSSList.push(o)
  }

  if (currentRSSList.length === 0) return message.channel.send('No feeds assigned to this channel.').catch(err => console.log(`Promise Warning: chooseFeed 1: ${err}`))

  const msgHandler = new MsgHandler(bot, message) // For deletion at the end of a series of menus
  if (firstMsgHandler) msgHandler.merge(firstMsgHandler)

  const pages = []
  for (var x in currentRSSList) {
    const count = parseInt(x, 10) + 1
    const link = currentRSSList[x].link
    const title = currentRSSList[x].title
    const status = currentRSSList[x].status || ''
    const miscOption = currentRSSList[x].titleChecks || currentRSSList[x].disableImgLinkPreviews || currentRSSList[x].disableImgLinks || ''

    // 7 feeds per embed (AKA page)
    if ((count - 1) !== 0 && (count - 1) / 7 % 1 === 0) {
      pages.push(embedMsg)
      embedMsg = new Discord.RichEmbed().setColor(config.botSettings.menuColor).setDescription(`Page ${pages.length + 1}`)
    }

    embedMsg.addField(`${count})  ${title}`, `${miscOption}${status}Link: ${link}`)
  }

  // Push the leftover results into the last embed
  pages.push(embedMsg)

  message.channel.send({embed: pages[0]})
  .then(m => {
    msgHandler.add(m)
    selectFeed()
    if (pages.length === 1) return
    m.react('◀').then(rct => {
      m.react('▶').then(rct2 => {
        pageControls.add(m.id, pages)
      }).catch(err => console.log(`Reaction Error: (${message.guild.id}, ${message.guild.name}) => Could not add emoticon >. Reason: `, err))
    }).catch(err => console.log(`Reaction Error: (${message.guild.id}, ${message.guild.name}) => Could not add emoticon <. Reason: `, err))
  }).catch(err => console.log(`Message Error: (${message.guild.id}, ${message.guild.name}) => Could not send message of embed feed selection list. Reason: `, err))

  // Only start message collector if all pages were sent
  function selectFeed () {
    const filter = m => m.author.id === message.author.id
    const collector = message.channel.createMessageCollector(filter, {time: 60000})
    channelTracker.add(message.channel.id)

    collector.on('collect', function (m) {
      msgHandler.add(m)
      let chosenOption = m.content
      if (chosenOption.toLowerCase() === 'exit') return collector.stop('Feed selection menu closed.')

      // Return an array of selected indices for feed removal
      if (commandList[command].action === 'Feed Removal') {
        let rawArray = chosenOption.split(',')

        for (var p = rawArray.length - 1; p >= 0; p--) { // Sanitize the input
          rawArray[p] = rawArray[p].trim()
          if (!rawArray[p]) rawArray.splice(p, 1)
        }

        let chosenOptionList = rawArray.filter(function (elem, index, self) { // Remove duplicates
          return index.toString() === self.indexOf(elem).toString()
        })

        let validChosens = []
        let invalidChosens = []

        for (var z in chosenOptionList) {
          let index = parseInt(chosenOptionList[z], 10) - 1
          if (isNaN(index) || index + 1 > currentRSSList.length || index + 1 < 1) invalidChosens.push(chosenOptionList[z])
          else validChosens.push(index)
        }

        if (invalidChosens.length > 0) return message.channel.send(`The number(s) \`${invalidChosens}\` are invalid. Try again.`).then(m => msgHandler.add(m)).catch(err => console.log(`Promise Warning: chooseFeed 4: ${err}`))
        else {
          collector.stop()
          for (var q in validChosens) {
            validChosens[q] = currentRSSList[validChosens[q]].rssName
          }
          return callback(validChosens, msgHandler)
        }
      }

      // Return a single index for non feed removal actions
      const index = parseInt(chosenOption, 10) - 1

      if (isNaN(index) || index + 1 > currentRSSList.length || index + 1 < 1) return message.channel.send('That is not a valid number. Try again.').then(m => msgHandler.add(m)).catch(err => console.log(`Promise Warning: chooseFeed 4: ${err}`))

      collector.stop()
      callback(currentRSSList[index].rssName, msgHandler)
    })
    collector.on('end', function (collected, reason) {
      channelTracker.remove(message.channel.id)
      if (reason === 'user') return
      if (reason === 'time') message.channel.send(`I have closed the menu due to inactivity.`)
      else if (reason !== 'user') message.channel.send(reason).then(m => m.delete(6000))
      msgHandler.deleteAll(message.channel)
    })
  }
}
<|endoftext|>


<commit_before>"use strict"

const functions = require('firebase-functions')
const admin = require("firebase-admin")

admin.initializeApp(functions.config().firebase);

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((req, res) => {
    res.send("Hello from Firebase!");
});

const second = 1000
const minute = 60 * second

exports.testPush = functions.https.onRequest((req, res) => {
    // admin.database().ref('/messages').push({original: original}).then(snapshot => {
    //     // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
    //     res.redirect(303, snapshot.ref);
    // });
    return admin.database().ref("/users").once("value")
                .then(sendTestPushToAllUsersWithRegistrationToken)
                .then(arr => res.send(arr))

    /*return admin.messaging()
     .sendToDevice(
     "fs_Fhp9WAGc:APA91bH9WAT9nfgn1XJhrkZtxojLOlX3o6fdA6oJ0U2ZMPaOFrDzfzy7VVuwOpUeT2YGrHf2eN63arSVwkDpZeXywP5bpgYT-ntJfyf1bcwiErmF72Uh2Bi__nlO61L0oOXxSDEsLRWs",
     {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
     {timeToLive: (10 * minute), priority: "high"}
     )
     .then(() => res.send("push sent"))*/
});

exports.monitorPushRequests =
    functions
        .database.ref('/users/{uid}/notifications/{todoId}')
        .onWrite(event => {
            const todo = event.data.val()
            if (!todo) return;
            const uid = event.params.uid
            const todoId = event.params.todoId
            const hasReminder = todo.reminder && todo.reminder.at
            const notificationRef = admin.database().ref("/notifications/" + uid + "---" + todoId)
            if (hasReminder) {
                return notificationRef.set({uid: uid, todo: todo, time: todo.reminder.at})
            } else {
                return Promise.all([event.data.ref.set(null), notificationRef.set(null)])
            }
        })

function sendTestPushToAllUsersWithRegistrationToken(userMap) {
    const promiseList = []
    userMap.forEach(function (userEntry) {
        const userId = userEntry.key
        const userData = userEntry.val()
        if (userData.token) {
            const promise = admin
                .messaging()
                .sendToDevice(
                    userData.token,
                    {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
                    {timeToLive: (10 * minute), priority: "high"}
                )
            promiseList.push(promise)
        }
    })
    return Promise.all(promiseList)
}

exports.notificationCorn = functions.https.onRequest((req, res) => {
    return admin
        .database().ref("/notifications")
        .orderByChild("time")
        .endAt(Date.now() + (10 * minute))
        .once("value")
        .then(sendPushNotifications)
        .then(arr => res.send(arr))

})

function sendPushNotifications(notificationMap) {
    const promiseList = []
    notificationMap.forEach(notificationEntry => {
        const notificationData = notificationEntry.val()
        const uid = notificationData.uid
        promiseList.push(
            admin.database().ref("/users/" + uid + "/token").once("value")
                 .then(sendPush(notificationData))
        )
    })
    return Promise.all(promiseList)
}

const sendPush = notificationData => tokenSnapshot => {
    const token = tokenSnapshot.val()
    const {id, text} = notificationData.todo
    if (token) {
        return admin
            .messaging()
            .sendToDevice(
                token,
                {data: {id, text}},
                {timeToLive: (10 * minute), priority: "high"}
            )
    } else {
        return Promise.resolve({
            error: "Cannot send notification: token not found: ",
            notificationData: notificationData
        })
    }
}
<commit_msg>scheduleReminderPush<commit_after>"use strict"

const functions = require('firebase-functions')
const admin = require("firebase-admin")

admin.initializeApp(functions.config().firebase);

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((req, res) => {
    res.send("Hello from Firebase!");
});

const second = 1000
const minute = 60 * second

exports.testPush = functions.https.onRequest((req, res) => {
    // admin.database().ref('/messages').push({original: original}).then(snapshot => {
    //     // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
    //     res.redirect(303, snapshot.ref);
    // });
    return admin.database().ref("/users").once("value")
                .then(sendTestPushToAllUsersWithRegistrationToken)
                .then(arr => res.send(arr))

    /*return admin.messaging()
     .sendToDevice(
     "fs_Fhp9WAGc:APA91bH9WAT9nfgn1XJhrkZtxojLOlX3o6fdA6oJ0U2ZMPaOFrDzfzy7VVuwOpUeT2YGrHf2eN63arSVwkDpZeXywP5bpgYT-ntJfyf1bcwiErmF72Uh2Bi__nlO61L0oOXxSDEsLRWs",
     {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
     {timeToLive: (10 * minute), priority: "high"}
     )
     .then(() => res.send("push sent"))*/
});

exports.monitorPushRequests =
    functions
        .database.ref('/users/{uid}/notifications/{todoId}')
        .onWrite(event => {
            const todo = event.data.val()
            if (!todo) return;
            const uid = event.params.uid
            const todoId = event.params.todoId
            const hasReminder = todo.reminder && todo.reminder.at
            const notificationRef = admin.database().ref("/notifications/" + uid + "---" + todoId)
            if (hasReminder) {
                return notificationRef.set({uid: uid, todo: todo, time: todo.reminder.at})
            } else {
                return Promise.all([event.data.ref.set(null), notificationRef.set(null)])
            }
        })

function sendTestPushToAllUsersWithRegistrationToken(userMap) {
    const promiseList = []
    userMap.forEach(function (userEntry) {
        const userId = userEntry.key
        const userData = userEntry.val()
        if (userData.token) {
            const promise = admin
                .messaging()
                .sendToDevice(
                    userData.token,
                    {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
                    {timeToLive: (10 * minute), priority: "high"}
                )
            promiseList.push(promise)
        }
    })
    return Promise.all(promiseList)
}

exports.notificationCorn = functions.https.onRequest((req, res) => {
    return admin
        .database().ref("/notifications")
        .orderByChild("time")
        .endAt(Date.now() + (10 * minute))
        .once("value")
        .then(sendPushNotifications)
        .then(arr => res.send(arr))

})

function sendPushNotifications(notificationMap) {
    const promiseList = []
    notificationMap.forEach(notificationEntry => {
        const notificationData = notificationEntry.val()
        const uid = notificationData.uid
        promiseList.push(
            admin.database().ref("/users/" + uid + "/token").once("value")
                 .then(sendPush(notificationData))
        )
    })
    return Promise.all(promiseList)
}

const sendPush = notificationData => tokenSnapshot => {
    const token = tokenSnapshot.val()
    const {_id, text} = notificationData.todo
    if (token) {
        return admin
            .messaging()
            .sendToDevice(
                token,
                {data: {_id, text}},
                {timeToLive: (10 * minute), priority: "high"}
            )
    } else {
        return Promise.resolve({
            error: "Cannot send notification: token not found: ",
            notificationData: notificationData
        })
    }
}
<|endoftext|>


<commit_before>const request = require('request');
const discord = require('discord.js');

let weatherConfig;
const geocodeEndpoint = 'https://maps.googleapis.com/maps/api/geocode/json?key=gkey&address=input';
const darkskyEndpoint = 'https://api.darksky.net/forecast/key/lat,lng';

function fahrenheitToCelcius(degree) {
  return (((degree - 32) * 5) / 9).toFixed(0);
}

module.exports = {
  usage: [
    'weather <address> - brings current weather info for given address',
    'weather -v <address> - brings additional weather info for given address',
  ],

  init: (bot) => {
    weatherConfig = bot.settings.weather;
  },

  run: (bot, message, cmdArgs) => {
    if (!cmdArgs) return true;
    let verbose = false;
    let args = cmdArgs.split(' ');
    if (args[0] === '-v') {
      verbose = true;
      args = args.slice(1);
    }
    args = args.join(' ');
    // get geocode info
    let requestURL = geocodeEndpoint
      .replace('gkey', weatherConfig.geocode_api_key || bot.settings.tokens.google_geocode)
      .replace('input', args);
    // do percentage encoding
    requestURL = encodeURI(requestURL);

    request(requestURL, (err, res, bod) => {
      if (err) {
        message.chanel.sendMessage('There was a problem with geocoding request.');
        return;
      }

      const geocodeData = JSON.parse(bod);

      if (geocodeData.status !== 'OK') {
        message.channel.sendMessage(`I'm sorry ${message.author}, your address couldn't be detected.`);
        return;
      }

      const address = geocodeData.results[0].formatted_address;
      const coordinate = geocodeData.results[0].geometry.location;
      // get weather data
      requestURL = darkskyEndpoint
        .replace('key', weatherConfig.darksky_api_key || bot.settings.tokens.darksky)
        .replace('lat', coordinate.lat)
        .replace('lng', coordinate.lng);

      request(requestURL, (error, response, body) => {
        const weatherData = JSON.parse(body);

        const offset = weatherData.offset;
        const utcTime = weatherData.currently.time;
        // datetime is weird in javascript, please do change this part if you can
        const localTime = new Date(utcTime * 1000);
        localTime.setHours(localTime.getHours() + offset);
        let dateString;
        // toGMTString prints out timezone of host so we slice it off
        if (offset > 0) {
          dateString = localTime.toUTCString() + ' +' + offset;
        } else {
          dateString = localTime.toUTCString() + ' ' + offset;
        }
        const temperatureF = weatherData.currently.temperature.toFixed(0);
        const temperatureC = fahrenheitToCelcius(temperatureF);
        const summary = weatherData.currently.summary;
        const humidity = (weatherData.currently.humidity * 100).toFixed(0);
        // convert speed to freedom units
        const windSpeed = (weatherData.currently.windSpeed * 1.61).toFixed(0);
        const pressure = weatherData.currently.pressure.toFixed(0);
        const embed = new discord.RichEmbed();

        if (verbose) {
          embed.setColor('#4286f4')
            .setFooter(`Local Time: ${dateString}`)
            .setTitle(`Weather in ${address}`)
            .addField('Summary', summary)
            .addField('Temperature °C', `${temperatureC} °C`, true)
            .addField('Temperature °F', `${temperatureF} °F`, true)
            .addField('Timezone', weatherData.timezone, true)
            .addField('Humidity', `${humidity}%`, true)
            .addField('Wind Speed', `${windSpeed} km/h`, true)
            .addField('Air Pressure', `${pressure} mbar`, true)
            .setDescription(weatherConfig.icons[weatherData.currently.icon]);
        } else {
          embed.setColor('#4286f4')
            .setFooter(`Local Time: ${dateString}`)
            .setTitle(`Weather in ${address}`)
            .addField('Summary', summary)
            .addField('Temperature', `${temperatureC} °C / ${temperatureF} °F`, true)
            .addField('Humidity', `${humidity}%`, true)
            .setDescription(weatherConfig.icons[weatherData.currently.icon]);
        }

        message.channel.sendEmbed(embed);
      });
    });
    return false;
  },
};
<commit_msg>Refactored weather command<commit_after>const request = require('request-promise');
const discord = require('discord.js');

let weatherConfig;
let tokens;
const geocodeEndpoint = 'https://maps.googleapis.com/maps/api/geocode/json?key=gkey&address=input';
const darkskyEndpoint = 'https://api.darksky.net/forecast/key/lat,lng';

function fahrenheitToCelcius(degree) {
  return (((degree - 32) * 5) / 9).toFixed(0);
}

function getGeocodeData(address) {
  let requestURL = geocodeEndpoint
    .replace('gkey', tokens.google_geocode)
    .replace('input', address);
  requestURL = encodeURI(requestURL);

  return request(requestURL).then((body) => {
    const geocodeData = JSON.parse(body);

    if (geocodeData.status !== 'OK') {
      // eslint-disable-next-line no-throw-literal
      throw 'I\'m sorry, the address couldn\'t be detected.';
    }

    const result = {
      address: geocodeData.results[0].formatted_address,
      coordinate: geocodeData.results[0].geometry.location,
    };

    return result;
  });
}

function getWeatherData(location) {
  const requestURL = darkskyEndpoint
    .replace('key', tokens.darksky)
    .replace('lat', location.coordinate.lat)
    .replace('lng', location.coordinate.lng);

  return request(requestURL).then((body) => {
    const weatherData = JSON.parse(body);

    const offset = weatherData.offset;
    const utcTime = weatherData.currently.time;
    // datetime is weird in javascript, please do change this part if you can
    const localTime = new Date(utcTime * 1000);
    localTime.setHours(localTime.getHours() + offset);
    let dateString;
    // toGMTString prints out timezone of host so we slice it off
    if (offset > 0) {
      dateString = localTime.toUTCString() + ' +' + offset;
    } else {
      dateString = localTime.toUTCString() + ' ' + offset;
    }

    const temperatureF = weatherData.currently.temperature.toFixed(0);
    const temperatureC = fahrenheitToCelcius(temperatureF);
    const summary = weatherData.currently.summary;
    const humidity = (weatherData.currently.humidity * 100).toFixed(0);
    // convert speed to freedom units
    const windSpeed = (weatherData.currently.windSpeed * 1.61).toFixed(0);
    const pressure = weatherData.currently.pressure.toFixed(0);
    const icon = weatherData.currently.icon;
    const timezone = weatherData.timezone;

    const result = {
      location,
      offset,
      localTime,
      dateString,
      temperatureC,
      temperatureF,
      summary,
      humidity,
      windSpeed,
      pressure,
      icon,
      timezone,
    };

    return result;
  });
}

function generateEmbed(data, verbose) {
  const embed = new discord.RichEmbed();

  if (verbose) {
    embed.setColor('#4286f4')
      .setFooter(`Local Time: ${data.dateString}`)
      .setTitle(`Weather in ${data.address}`)
      .addField('Summary', data.summary)
      .addField('Temperature °C', `${data.temperatureC} °C`, true)
      .addField('Temperature °F', `${data.temperatureF} °F`, true)
      .addField('Timezone', data.timezone, true)
      .addField('Humidity', `${data.humidity}%`, true)
      .addField('Wind Speed', `${data.windSpeed} km/h`, true)
      .addField('Air Pressure', `${data.pressure} mbar`, true)
      .setDescription(weatherConfig.icons[data.icon]);
  } else {
    embed.setColor('#4286f4')
      .setFooter(`Local Time: ${data.dateString}`)
      .setTitle(`Weather in ${data.address}`)
      .addField('Summary', data.summary)
      .addField('Temperature', `${data.temperatureC} °C / ${data.temperatureF} °F`, true)
      .addField('Humidity', `${data.humidity}%`, true)
      .setDescription(weatherConfig.icons[data.icon]);
  }

  return embed;
}

module.exports = {
  usage: [
    'weather <address> - brings current weather info for given address',
    'weather -v <address> - brings additional weather info for given address',
  ],
  run: (bot, message, cmdArgs) => {
    if (!cmdArgs) return true;

    let args = cmdArgs.split(' ');
    const verbose = args[0] === '-v';
    args = verbose ? args.slice(1) : args;

    getGeocodeData(args).then(getWeatherData)
                        .then(data => generateEmbed(data, verbose))
                        .then(embed => message.channel.sendEmbed(embed))
                        .catch(err => message.channel.sendMessage(err.toString()));
    return false;
  },
  init: (bot) => {
    weatherConfig = bot.settings.weather;
    tokens = bot.settings.tokens;
  },
};
<|endoftext|>