<commit_before>const path = require('path');
const express = require('express');
const pug = require('pug');
const bodyParser = require('body-parser');
const moment = require('moment');
const crypto = require('crypto');
const base64url = require('base64-url');

const pjson = require('./package.json');
var program = require('commander');

const app = express();

var options = {
	port: 3000,
	host: '127.0.0.1',
	url: 'https://notica.us',
};

program.version(pjson.version)
	.option('-p, --port <3000>', 'Host port')
	.option('-H, --host <127.0.0.1>', 'Host IP')
	.option('-U, --url <https://notica.us>', 'Website URL');

program.on('--help', function() {
	console.log('');
	console.log('  Example:');
	console.log('');
	console.log('    $ npm start -- -p 80');
	console.log('');
});

program.parse(process.argv);

Object.keys(options).forEach(function(key) {
	options[key] = program[key] || options[key];
});

const host = options.host;
const port = options.port;
const url = options.url;

app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine', 'pug')

function log(message) {
	console.log(moment().format() + ': ' + message);
}

function generateID() {
	const bytes = crypto.randomBytes(30);
	const string = base64url.encode(bytes);
	return string.substring(0, 8);
}

app.use('/', express.static(path.join(__dirname, 'public')));
app.get('/*', (req, res) => {
	res.render('index', { secureID: generateID() })
});

app.post('*', (req, res) => {
	let id = req.path.substring(1, 20); // maintain backwards compat.
	id = id || Object.keys(req.query)[0] || '';
	let data = Object.keys(req.body)[0];

	if (data && data.substring(0,2) === 'd:') {
		if (io.sockets.adapter.rooms[id]) {
			let message = data.substring(2);

			log('[NOTICA] Message sent to ' + id + ': ' + message);

			io.in(id).emit('message', message);

			res.end();
		} else {
			log('No one in room to send data to: ' + id);
			res.send('No devices have that Notica ID open. Please open this URL: '
				+ url + '/?' + id + '\n');
		}
	} else {
		log('Ignoring bad POST data to: ' + id);
		res.send('Bad POST data. Expecting prefix of "d:".\n');
	}
});

const server = app.listen(port, host, (err) => {
	if (err) {
		log('[ERROR] Server error: ' + err);
		return;
	}
	console.info('==> Listening on port %s. Open up http://%s:%s/ in your browser.', port, host, port);
});

const io = require('socket.io').listen(server);

io.on('connection', (socket) => {
	var address = socket.handshake.address;
	log('New connection from ' + address);
	socket.on('room', (room) => {
		log('New connection joining room: ' + room);
		socket.join(room);
	});
});
<commit_msg>Added appropriate http status codes on errors<commit_after>const path = require('path');
const express = require('express');
const pug = require('pug');
const bodyParser = require('body-parser');
const moment = require('moment');
const crypto = require('crypto');
const base64url = require('base64-url');

const pjson = require('./package.json');
var program = require('commander');

const app = express();

var options = {
	port: 3000,
	host: '127.0.0.1',
	url: 'https://notica.us',
};

program.version(pjson.version)
	.option('-p, --port <3000>', 'Host port')
	.option('-H, --host <127.0.0.1>', 'Host IP')
	.option('-U, --url <https://notica.us>', 'Website URL');

program.on('--help', function() {
	console.log('');
	console.log('  Example:');
	console.log('');
	console.log('    $ npm start -- -p 80');
	console.log('');
});

program.parse(process.argv);

Object.keys(options).forEach(function(key) {
	options[key] = program[key] || options[key];
});

const host = options.host;
const port = options.port;
const url = options.url;

app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine', 'pug')

function log(message) {
	console.log(moment().format() + ': ' + message);
}

function generateID() {
	const bytes = crypto.randomBytes(30);
	const string = base64url.encode(bytes);
	return string.substring(0, 8);
}

app.use('/', express.static(path.join(__dirname, 'public')));
app.get('/*', (req, res) => {
	res.render('index', { secureID: generateID() })
});

app.post('*', (req, res) => {
	let id = req.path.substring(1, 20); // maintain backwards compat.
	id = id || Object.keys(req.query)[0] || '';
	let data = Object.keys(req.body)[0];

	if (data && data.substring(0,2) === 'd:') {
		if (io.sockets.adapter.rooms[id]) {
			let message = data.substring(2);

			log('[NOTICA] Message sent to ' + id + ': ' + message);

			io.in(id).emit('message', message);

			res.end();
		} else {
			log('No one in room to send data to: ' + id);
			res.status(404).send('No devices have that Notica ID open. Please open this URL: '
				+ url + '/?' + id + '\n');
		}
	} else {
		log('WTF')
		log('Ignoring bad POST data to: ' + id);
		res.status(400).send('Bad POST data. Expecting prefix of "d:".\n');
	}
});

const server = app.listen(port, host, (err) => {
	if (err) {
		log('[ERROR] Server error: ' + err);
		return;
	}
	console.info('==> Listening on port %s. Open up http://%s:%s/ in your browser.', port, host, port);
});

const io = require('socket.io').listen(server);

io.on('connection', (socket) => {
	var address = socket.handshake.address;
	log('New connection from ' + address);
	socket.on('room', (room) => {
		log('New connection joining room: ' + room);
		socket.join(room);
	});
});
<|endoftext|>


<commit_before>var pg = require('pg');
var zlib = require('zlib');
const SQL = require('sql-template-strings');

module.exports = function(options) {

	var pgPool;
	var lyr;

	/**
	 * Initializes the layer config and the PostgreSQL datasource.
	 *
	 * @param {TileServer} server
	 * @param {function} callback(err, fn)
	 * @return {void}
	 */
	function initialize(server, callback) {
		lyr = options.lyr;
		pgPool = new pg.Pool(options.pgConfig);
		pgPool.on('error', function (err, client) {
		  console.error(err.message, err.stack);
			var err = new Error('idle client error');
			err.statusCode = 500;
			callback(err);
		});
		if ((lyr.mode === 'cluster' || lyr.mode === 'cluster_fields' || (typeof lyr.mode === 'function')) && lyr.type != 'circle') {
			var err = new Error('Clustering and mode functions can only be used in conjunction with point data');
			err.statusCode = 422;
			callback(err);
		} else {
			callback(null);
		}
	}

	/**
	 * Creates a tile and returns the result as a Mapbox Vector Tile,
	 * plus the headers that should accompany it.
	 *
	 * @param {TileServer} server
	 * @param {TileRequest} tile
	 * @param {function} callback(err, buffer, headers)
	 * @return {void}
	 */
	function serveMVT(server, tile, callback) {
		if (tile.z < lyr.minZoom || tile.z > lyr.maxZoom) {
			err = new Error('Request out of zoom level bounds');
			err.statusCode = 204;
			return callback(err);
		}

		var fields = lyr.fields ? ', ' + lyr.fields.split(' ') : '';
		var clip_geom = (lyr.buffer > 0) ? true : false;
		var resolution = (typeof lyr.resolution === 'function') ? lyr.resolution(server, tile) : lyr.resolution;
		var mode = (typeof lyr.mode === 'function') ? lyr.mode(server, tile) : lyr.mode;

		var query;
		switch (mode) {

			case "cluster":
				var agg_q_name = 'mvt_geo';
				query = `
					SELECT ST_AsMVT('${tile.layer}', ${resolution}, 'geom', q) AS mvt FROM (
						WITH ${agg_q_name} AS (
							SELECT 1 cnt, ST_AsMVTGeom(ST_Transform(${lyr.table}.${lyr.geometry}, 3857), TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857), ${resolution}, ${lyr.buffer}, ${clip_geom}) geom
							FROM ${lyr.table}
							WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
						)
						SELECT COUNT(${agg_q_name}.cnt), ${agg_q_name}.geom
						FROM ${agg_q_name}
						GROUP BY ${agg_q_name}.geom
					) AS q
				`;
				break;

			case "cluster_fields":
				var agg_q_name = 'mvt_geo';
				var fieldsAgg = '';
				if (lyr.fields) {
					lyr.fields.split(' ').forEach(function(field) {
						fieldsAgg += ', string_agg('+agg_q_name+'.' + field + '::text, \',\') AS ' + field;
					});
				}
				query = `
				SELECT ST_AsMVT('${tile.layer}', ${resolution}, 'geom', q) AS mvt FROM (
					WITH ${agg_q_name} AS (
						SELECT 1 cnt, ST_AsMVTGeom(ST_Transform(${lyr.table}.${lyr.geometry}, 3857), TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857), ${resolution}, ${lyr.buffer}, ${clip_geom}) geom ${fields}
						FROM ${lyr.table}
						WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
					)
					SELECT COUNT(${agg_q_name}.cnt), ${agg_q_name}.geom ${fieldsAgg}
					FROM ${agg_q_name}
					GROUP BY ${agg_q_name}.geom
				) AS q
				`;
				break;

				default:
					query = `
						SELECT ST_AsMVT('${tile.layer}', ${resolution}, 'geom', q) AS mvt FROM (
                            WITH a AS (
							SELECT ST_AsMVTGeom(
                                ST_Transform(${lyr.table}.${lyr.geometry}, 3857),
                                TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857),
                                ${resolution},
                                ${lyr.buffer},
                                ${clip_geom} ) geom ${fields}
							FROM ${lyr.table}
							WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
                            )
                            SELECT * FROM a WHERE geom IS NOT NULL
						) AS q
					`;
					break;
		}

		pgPool.query(query, function(err, result) {
			if (err) {
				console.log(query, err.message, err.stack)
				var err = new Error('An error occurred');
				err.statusCode = 500;
				return callback(err);
			}
			if (!result.rows[0].mvt) {
				err = new Error('No data');
				err.statusCode = 204;
				return callback(err);
			}
            zlib.gzip(result.rows[0].mvt, function(err, result) {
                if (!err) {
                    callback(null, result, {'Content-Type': 'application/x-protobuf', 'Content-Encoding': 'gzip'});
                }
            });
		});
	}

	return {
		name: 'postgismvt',
    init: initialize,
		serve: serveMVT
	};
};
<commit_msg>Aligned ST_AsMVT signature to postgis/postgis@b29eea7<commit_after>var pg = require('pg');
var zlib = require('zlib');
const SQL = require('sql-template-strings');

module.exports = function(options) {

	var pgPool;
	var lyr;

	/**
	 * Initializes the layer config and the PostgreSQL datasource.
	 *
	 * @param {TileServer} server
	 * @param {function} callback(err, fn)
	 * @return {void}
	 */
	function initialize(server, callback) {
		lyr = options.lyr;
		pgPool = new pg.Pool(options.pgConfig);
		pgPool.on('error', function (err, client) {
		  console.error(err.message, err.stack);
			var err = new Error('idle client error');
			err.statusCode = 500;
			callback(err);
		});
		if ((lyr.mode === 'cluster' || lyr.mode === 'cluster_fields' || (typeof lyr.mode === 'function')) && lyr.type != 'circle') {
			var err = new Error('Clustering and mode functions can only be used in conjunction with point data');
			err.statusCode = 422;
			callback(err);
		} else {
			callback(null);
		}
	}

	/**
	 * Creates a tile and returns the result as a Mapbox Vector Tile,
	 * plus the headers that should accompany it.
	 *
	 * @param {TileServer} server
	 * @param {TileRequest} tile
	 * @param {function} callback(err, buffer, headers)
	 * @return {void}
	 */
	function serveMVT(server, tile, callback) {
		if (tile.z < lyr.minZoom || tile.z > lyr.maxZoom) {
			err = new Error('Request out of zoom level bounds');
			err.statusCode = 204;
			return callback(err);
		}

		var fields = lyr.fields ? ', ' + lyr.fields.split(' ') : '';
		var clip_geom = (lyr.buffer > 0) ? true : false;
		var resolution = (typeof lyr.resolution === 'function') ? lyr.resolution(server, tile) : lyr.resolution;
		var mode = (typeof lyr.mode === 'function') ? lyr.mode(server, tile) : lyr.mode;

		var query;
		switch (mode) {

			case "cluster":
				var agg_q_name = 'mvt_geo';
				query = `
					SELECT ST_AsMVT(q, '${tile.layer}', ${resolution}, 'geom') AS mvt FROM (
						WITH ${agg_q_name} AS (
							SELECT 1 cnt, ST_AsMVTGeom(ST_Transform(${lyr.table}.${lyr.geometry}, 3857), TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857), ${resolution}, ${lyr.buffer}, ${clip_geom}) geom
							FROM ${lyr.table}
							WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
						)
						SELECT COUNT(${agg_q_name}.cnt), ${agg_q_name}.geom
						FROM ${agg_q_name}
						GROUP BY ${agg_q_name}.geom
					) AS q
				`;
				break;

			case "cluster_fields":
				var agg_q_name = 'mvt_geo';
				var fieldsAgg = '';
				if (lyr.fields) {
					lyr.fields.split(' ').forEach(function(field) {
						fieldsAgg += ', string_agg('+agg_q_name+'.' + field + '::text, \',\') AS ' + field;
					});
				}
				query = `
				SELECT ST_AsMVT(q, '${tile.layer}', ${resolution}, 'geom') AS mvt FROM (
					WITH ${agg_q_name} AS (
						SELECT 1 cnt, ST_AsMVTGeom(ST_Transform(${lyr.table}.${lyr.geometry}, 3857), TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857), ${resolution}, ${lyr.buffer}, ${clip_geom}) geom ${fields}
						FROM ${lyr.table}
						WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
					)
					SELECT COUNT(${agg_q_name}.cnt), ${agg_q_name}.geom ${fieldsAgg}
					FROM ${agg_q_name}
					GROUP BY ${agg_q_name}.geom
				) AS q
				`;
				break;

				default:
					query = `
						SELECT ST_AsMVT(q, '${tile.layer}', ${resolution}, 'geom') AS mvt FROM (
                            WITH a AS (
							SELECT ST_AsMVTGeom(
                                ST_Transform(${lyr.table}.${lyr.geometry}, 3857),
                                TileBBox(${tile.z}, ${tile.x}, ${tile.y}, 3857),
                                ${resolution},
                                ${lyr.buffer},
                                ${clip_geom} ) geom ${fields}
							FROM ${lyr.table}
							WHERE ST_Intersects(TileBBox(${tile.z}, ${tile.x}, ${tile.y}, ${lyr.srid}), ${lyr.table}.${lyr.geometry})
                            )
                            SELECT * FROM a WHERE geom IS NOT NULL
						) AS q
					`;
					break;
		}

		pgPool.query(query, function(err, result) {
			if (err) {
				console.log(query, err.message, err.stack)
				var err = new Error('An error occurred');
				err.statusCode = 500;
				return callback(err);
			}
			if (!result.rows[0].mvt) {
				err = new Error('No data');
				err.statusCode = 204;
				return callback(err);
			}
            zlib.gzip(result.rows[0].mvt, function(err, result) {
                if (!err) {
                    callback(null, result, {'Content-Type': 'application/x-protobuf', 'Content-Encoding': 'gzip'});
                }
            });
		});
	}

	return {
		name: 'postgismvt',
    init: initialize,
		serve: serveMVT
	};
};
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var app = angular.module('radar.patients');

  function controllerFactory(
    ListController,
    $injector,
    ListHelperProxy,
    firstPromise,
    store
  ) {
    function PatientListController($scope) {
      var self = this;

      $injector.invoke(ListController, self, {$scope: $scope});

      var defaultFilters = {
        isActive: true
      };
      $scope.filters = angular.copy(defaultFilters);

      var proxy = new ListHelperProxy(search, {
        perPage: 50,
        sortBy: 'id',
        reverse: true
      });
      proxy.load();

      $scope.proxy = proxy;
      $scope.search = search;
      $scope.clear = clear;
      $scope.count = 0;

      function search() {
        var proxyParams = proxy.getParams();
        var params = angular.extend({}, proxyParams, $scope.filters);

        self.load(firstPromise([
          store.findMany('patients', params, true).then(function(data) {
            proxy.setItems(data.data);
            proxy.setCount(data.pagination.count);
            $scope.count = data.pagination.count;
            return data.data;
          }),
          store.findMany('genders').then(function(genders) {
            $scope.genders = genders;
          })
        ]));
      }

      function clear() {
        $scope.filters = angular.copy(defaultFilters);
        search();
      }
    }

    PatientListController.$inject = ['$scope'];
    PatientListController.prototype = Object.create(ListController.prototype);

    return PatientListController;
  }

  controllerFactory.$inject = [
    'ListController',
    '$injector',
    'ListHelperProxy',
    'firstPromise',
    'store'
  ];

  app.factory('PatientListController', controllerFactory);
})();
<commit_msg>Sort by recruited_date not id<commit_after>(function() {
  'use strict';

  var app = angular.module('radar.patients');

  function controllerFactory(
    ListController,
    $injector,
    ListHelperProxy,
    firstPromise,
    store
  ) {
    function PatientListController($scope) {
      var self = this;

      $injector.invoke(ListController, self, {$scope: $scope});

      var defaultFilters = {
        isActive: true
      };
      $scope.filters = angular.copy(defaultFilters);

      var proxy = new ListHelperProxy(search, {
        perPage: 50,
        sortBy: 'recruitedDate',
        reverse: true
      });
      proxy.load();

      $scope.proxy = proxy;
      $scope.search = search;
      $scope.clear = clear;
      $scope.count = 0;

      function search() {
        var proxyParams = proxy.getParams();
        var params = angular.extend({}, proxyParams, $scope.filters);

        self.load(firstPromise([
          store.findMany('patients', params, true).then(function(data) {
            proxy.setItems(data.data);
            proxy.setCount(data.pagination.count);
            $scope.count = data.pagination.count;
            return data.data;
          }),
          store.findMany('genders').then(function(genders) {
            $scope.genders = genders;
          })
        ]));
      }

      function clear() {
        $scope.filters = angular.copy(defaultFilters);
        search();
      }
    }

    PatientListController.$inject = ['$scope'];
    PatientListController.prototype = Object.create(ListController.prototype);

    return PatientListController;
  }

  controllerFactory.$inject = [
    'ListController',
    '$injector',
    'ListHelperProxy',
    'firstPromise',
    'store'
  ];

  app.factory('PatientListController', controllerFactory);
})();
<|endoftext|>


<commit_before>const ChampionImage = React.createClass({
  getClassName: function () {
    return this.props.isSelected ? 'champion-selected' : ''
  },
  onClick: function () {
    this.props.updateSelection()
  },
  render: function () {
    return (
      <img
        onClick={ this.onClick }
        className={ `champion ${this.getClassName()}` }
        src={ "http://ddragon.leagueoflegends.com/cdn/6.9.1/img/champion/" + this.props.championName + ".png" }
        alt={ this.props.championName }/>
    )
  }
})

const ChampionSearch = React.createClass({
  onChange: function () {
    let query = this.refs.search.value
    this.props.search(query)
  },
  componentWillMount: function () {
    this.onChange = _.debounce(this.onChange, 100)
  },
  render: function () {
    return (

        <input type="text" className="form-control" placeholder="Search" ref="search" onChange={this.onChange}/>

    )
  }
})

const ChampionStats = React.createClass({
  getInitialState: function () {
    return { champion: {} }
  },
  componentWillReceiveProps: function (props) {
    $.get(`api/champion/${props.champion}`, champion => this.setState({champion}))
  },
  stats: function () {
    const humanStats = { attackspeedoffset: 'Attac Speed Offset'
                       , attackrange: 'Attack Range'
                       , mpregenperlevel: 'MP Regen per Level'
                       , mpperlevel: 'MP per Level'
                       , mpregen: 'MP Regen'
                       , hpregenperlevel: 'HP Regen per Level'
                       , attackdamageperlevel: 'Attack Damage per Level'
                       , attackspeedperlevel: 'Attack Speed per Level'
                       , crit: 'Crit'
                       , hpperlevel: 'HP per Level'
                       , movespeed: 'Move Speed'
                       , spellblockperlevel: 'Magic Resist per Level'
                       , armorperlevel: 'Armor per Level'
                       , hpregen: 'HP Regen'
                       , armor: 'Armor'
                       , spellblock: 'Magic Resist'
                       , attackdamage: 'Attack Damage'
                       , hp: 'HP'
                       , mp: 'MP'
                       , critperlevel: 'Crit per Level'
                       }
    let champion = this.state.champion
    return _.mapKeys(champion.stats, (value, key) => humanStats[key])
  },
  blurb: function () {
    return {__html: this.state.champion.blurb}
  },
  render: function () {
    let champion = this.state.champion
    window.champion = champion
    let championDescription =

    if(!_.isEmpty(champion)) {
      let championStats =
        _.transform(this.stats(), (result, value, stat) => {
        result.push(

            {stat}
            {value}

        )
      }, [])

      championDescription = (

            <img className="img-responsive"
                 src={`http://ddragon.leagueoflegends.com/cdn/img/champion/loading/${champion.key}_0.jpg`}
                 alt={champion.name} />

              {championStats}

      )
    }

    return championDescription
  }
})

const ChampionsGrid = React.createClass({
  getInitialState: function () {
    return {champions: []}
  },
  componentDidMount: function () {
    $.get('api/champions-by-name', champions => this.setState({champions}))
  },
  filterChampions: function (query) {
    this.setState({
      filteredChampions: this.state.champions.filter(champion => champion.name.toLowerCase().indexOf(query.toLowerCase()) != -1)
    })
  },
  render: function () {
    let selectedChampion = this.props.selectedChampion
    let setSelectedChampion = this.props.setSelectedChampion

    let champions = this.state.filteredChampions ? this.state.filteredChampions : this.state.champions

    return (

            <ChampionSearch search={this.filterChampions} />

            {champions.map(function (champion) {
              return <ChampionImage
                key={ champion.id }
                isSelected={ champion.id === selectedChampion }
                updateSelection={ setSelectedChampion.bind(null, champion.id) }
                championName={ champion.key }/>
            })}

    )
  }
})

window.ChampionsGrid = ChampionsGrid
window.ChampionStats = ChampionStats<commit_msg>rearrange the humanstats but it didn't matter :'(<commit_after>const ChampionImage = React.createClass({
  getClassName: function () {
    return this.props.isSelected ? 'champion-selected' : ''
  },
  onClick: function () {
    this.props.updateSelection()
  },
  render: function () {
    return (
      <img
        onClick={ this.onClick }
        className={ `champion ${this.getClassName()}` }
        src={ "http://ddragon.leagueoflegends.com/cdn/6.9.1/img/champion/" + this.props.championName + ".png" }
        alt={ this.props.championName }/>
    )
  }
})

const ChampionSearch = React.createClass({
  onChange: function () {
    let query = this.refs.search.value
    this.props.search(query)
  },
  componentWillMount: function () {
    this.onChange = _.debounce(this.onChange, 100)
  },
  render: function () {
    return (

        <input type="text" className="form-control" placeholder="Search" ref="search" onChange={this.onChange}/>

    )
  }
})

const ChampionStats = React.createClass({
  getInitialState: function () {
    return { champion: {} }
  },
  componentWillReceiveProps: function (props) {
    $.get(`api/champion/${props.champion}`, champion => this.setState({champion}))
  },
  stats: function () {
    const humanStats = { hp: 'HP'
                       , mp: 'MP'
                       , hpregen: 'HP Regen'
                       , hpregenperlevel: 'HP Regen per Level'
                       , mpregen: 'MP Regen'
                       , mpregenperlevel: 'MP Regen per Level'
                       , mpperlevel: 'MP per Level'
                       , movespeed: 'Move Speed'
                       , attackspeedoffset: 'Attack Speed Offset'
                       , attackrange: 'Attack Range'
                       , attackdamageperlevel: 'Attack Damage per Level'
                       , attackspeedperlevel: 'Attack Speed per Level'
                       , crit: 'Crit'
                       , hpperlevel: 'HP per Level'
                       , spellblockperlevel: 'Magic Resist per Level'
                       , armorperlevel: 'Armor per Level'
                       , armor: 'Armor'
                       , spellblock: 'Magic Resist'
                       , attackdamage: 'Attack Damage'
                       , critperlevel: 'Crit per Level'
                       }
    let champion = this.state.champion
    return _.mapKeys(champion.stats, (value, key) => humanStats[key])
  },
  blurb: function () {
    return {__html: this.state.champion.blurb}
  },
  render: function () {
    let champion = this.state.champion
    let championDescription =

    if(!_.isEmpty(champion)) {
      let championStats =
        _.transform(this.stats(), (result, value, stat) => {
        result.push(

            {stat}
            {value}

        )
      }, [])

      championDescription = (

            <img className="img-responsive"
                 src={`http://ddragon.leagueoflegends.com/cdn/img/champion/loading/${champion.key}_0.jpg`}
                 alt={champion.name} />

              {championStats}

      )
    }

    return championDescription
  }
})

const ChampionsGrid = React.createClass({
  getInitialState: function () {
    return {champions: []}
  },
  componentDidMount: function () {
    $.get('api/champions-by-name', champions => this.setState({champions}))
  },
  filterChampions: function (query) {
    this.setState({
      filteredChampions: this.state.champions.filter(champion => champion.name.toLowerCase().indexOf(query.toLowerCase()) != -1)
    })
  },
  render: function () {
    let selectedChampion = this.props.selectedChampion
    let setSelectedChampion = this.props.setSelectedChampion

    let champions = this.state.filteredChampions ? this.state.filteredChampions : this.state.champions

    return (

            <ChampionSearch search={this.filterChampions} />

            {champions.map(function (champion) {
              return <ChampionImage
                key={ champion.id }
                isSelected={ champion.id === selectedChampion }
                updateSelection={ setSelectedChampion.bind(null, champion.id) }
                championName={ champion.key }/>
            })}

    )
  }
})

window.ChampionsGrid = ChampionsGrid
window.ChampionStats = ChampionStats<|endoftext|>


<commit_before>/*jshint esversion: 6 */
(function() {
  'use strict';

  const path = require('path');
  const express = require('express');
  const webpack = require('webpack');
  const passport = require('passport');
  const session = require('express-session');
  const bodyParser = require('body-parser');
  const methodOverride = require('method-override');
  const GitHubStrategy = require('passport-github2').Strategy;
  const partials = require('express-partials');
  let config;
  let CONFIG;
  let SECRET;
  let GH_ID;
  let GH_SECRET;

  const isDeveloping = process.env.NODE_ENV !== 'production';
  const port = isDeveloping ? 3000 : process.env.PORT;
  const app = express();

  // configure Express
  app.use(partials());
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());
  app.use(methodOverride());

  if (isDeveloping) {
    config = require('./webpack.config.js');
    CONFIG = require('./config/config');
    SECRET = CONFIG.SESSION_SECRET;
    GH_ID = CONFIG.GITHUB.ID;
    GH_SECRET = CONFIG.GITHUB.SECRET;
  } else {
    config = require('./webpack.production.config.js');
    SECRET = process.env.SESSION_SECRET;
    GH_ID = process.env.GITHUB_ID;
    GH_SECRET = process.env.GITHUB_SECRET;
  }

  app.use(session({
    secret: SECRET,
    resave: false,
    saveUninitialized: false
  }));
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(express.static(__dirname + '/public'));

  passport.serializeUser(function(user, done) {
    done(null, user);
  });

  passport.deserializeUser(function(obj, done) {
    done(null, obj);
  });

  passport.use(new GitHubStrategy({
    clientID: GH_ID,
    clientSecret: GH_SECRET,
    callbackURL: 'http://0.0.0.0:3000/auth/github/callback'
  },
  function(accessToken, refreshToken, profile, done) {
    profile.accessToken = accessToken;
    process.nextTick(function () {
      return done(null, profile);
    });
  }));

  app.get('/auth/github',
    passport.authenticate('github', { scope: [ 'gist' ] }),
    function(req, res) {
    });

  app.get('/auth/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
      res.redirect(
        '/?token=' + req.user.accessToken +
        '&username=' + req.user.username +
        '&id=' + req.user.id
      );
    });

  app.get('/logout', (req, res) => {
    req.logout();
    res.redirect('/');
  });

  if (isDeveloping) {
    const webpackMiddleware = require('webpack-dev-middleware');
    const webpackHotMiddleware = require('webpack-hot-middleware');
    const compiler = webpack(config);
    const middleware = webpackMiddleware(compiler, {
      publicPath: config.output.publicPath,
      contentBase: 'src',
      stats: {
        colors: true,
        hash: false,
        timings: true,
        chunks: false,
        chunkModules: false,
        modules: false
      }
    });

    app.use(middleware);
    app.use(webpackHotMiddleware(compiler));
    app.get('*', function response(req, res) {
        res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
        res.end();
      });
  } else {
    app.use(express.static(__dirname + '/dist'));
    app.get('*', function response(req, res) {
      res.sendFile(path.join(__dirname, 'dist/index.html'));
    });
  }

  app.listen(port, '0.0.0.0', function onStart(err) {
    if (err) {
      console.error(err);
    }
    console.info('==> 🌎 Listening on port %s.' +
      'Open up http://0.0.0.0:%s/ in your browser.', port, port);
  });
}());
<commit_msg>update.<commit_after>/*jshint esversion: 6 */
(function() {
  'use strict';

  const path = require('path');
  const express = require('express');
  const webpack = require('webpack');
  const passport = require('passport');
  const session = require('express-session');
  const bodyParser = require('body-parser');
  const methodOverride = require('method-override');
  const GitHubStrategy = require('passport-github2').Strategy;
  const partials = require('express-partials');
  let config;
  let CONFIG;
  let SECRET;
  let GH_ID;
  let GH_SECRET;

  const isDeveloping = process.env.NODE_ENV !== 'production';
  const port = isDeveloping ? 3000 : process.env.PORT;
  const app = express();

  // configure Express
  app.use(partials());
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());
  app.use(methodOverride());

  if (isDeveloping) {
    config = require('./webpack.config.js');
    CONFIG = require('./config/config');
    SECRET = CONFIG.SESSION_SECRET;
    GH_ID = CONFIG.GITHUB.ID;
    GH_SECRET = CONFIG.GITHUB.SECRET;
  } else {
    config = require('./webpack.production.config.js');
    SECRET = process.env.SESSION_SECRET;
    GH_ID = process.env.GITHUB_ID;
    GH_SECRET = process.env.GITHUB_SECRET;
  }

  app.use(session({
    secret: SECRET,
    resave: false,
    saveUninitialized: false
  }));
  app.use(passport.initialize());
  app.use(passport.session());
  // app.use(express.static(__dirname + '/public'));

  passport.serializeUser(function(user, done) {
    done(null, user);
  });

  passport.deserializeUser(function(obj, done) {
    done(null, obj);
  });

  passport.use(new GitHubStrategy({
    clientID: GH_ID,
    clientSecret: GH_SECRET,
    callbackURL: 'http://0.0.0.0:3000/auth/github/callback'
  },
  function(accessToken, refreshToken, profile, done) {
    profile.accessToken = accessToken;
    process.nextTick(function () {
      return done(null, profile);
    });
  }));

  app.get('/auth/github',
    passport.authenticate('github', { scope: [ 'gist' ] }),
    function(req, res) {
    });

  app.get('/auth/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
      res.redirect(
        '/?token=' + req.user.accessToken +
        '&username=' + req.user.username +
        '&id=' + req.user.id
      );
    });

  app.get('/logout', (req, res) => {
    req.logout();
    res.redirect('/');
  });

  if (isDeveloping) {
    const webpackMiddleware = require('webpack-dev-middleware');
    const webpackHotMiddleware = require('webpack-hot-middleware');
    const compiler = webpack(config);
    const middleware = webpackMiddleware(compiler, {
      publicPath: config.output.publicPath,
      contentBase: 'src',
      stats: {
        colors: true,
        hash: false,
        timings: true,
        chunks: false,
        chunkModules: false,
        modules: false
      }
    });

    app.use(middleware);
    app.use(webpackHotMiddleware(compiler));
    app.get('*', function response(req, res) {
        res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
        res.end();
      });
  } else {
    app.use(express.static(__dirname + '/dist'));
    app.get('*', function response(req, res) {
      res.sendFile(path.join(__dirname, 'dist/index.html'));
    });
  }

  app.listen(port, '0.0.0.0', function onStart(err) {
    if (err) {
      console.error(err);
    }
    console.info('==> 🌎 Listening on port %s.' +
      'Open up http://0.0.0.0:%s/ in your browser.', port, port);
  });
}());
<|endoftext|>


<commit_before>/* @flow */

import React from 'react'
import PropTypes from 'prop-types'
import ReactNative, {
  Keyboard,
  Platform,
  UIManager,
  TextInput,
  findNodeHandle,
  Animated
} from 'react-native'
import { isIphoneX } from 'react-native-iphone-x-helper'
import type { KeyboardAwareInterface } from './KeyboardAwareInterface'

const _KAM_DEFAULT_TAB_BAR_HEIGHT: number = isIphoneX() ? 83 : 49
const _KAM_KEYBOARD_OPENING_TIME: number = 250
const _KAM_EXTRA_HEIGHT: number = 75

const supportedKeyboardEvents = [
  'keyboardWillShow',
  'keyboardDidShow',
  'keyboardWillHide',
  'keyboardDidHide',
  'keyboardWillChangeFrame',
  'keyboardDidChangeFrame',
]
const keyboardEventToCallbackName = (eventName: string) => (
  'on' + eventName[0].toUpperCase() + eventName.substring(1)
)
const keyboardEventPropTypes = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => (
    { ...acc, [keyboardEventToCallbackName(eventName)]: PropTypes.func }
  ), {}
)
const keyboardAwareHOCTypeEvents = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => (
    { ...acc, [keyboardEventToCallbackName(eventName)]: Function }
  ), {}
)

export type KeyboardAwareHOCProps = {
  viewIsInsideTabBar?: boolean,
  resetScrollToCoords?: {
    x: number,
    y: number
  },
  enableResetScrollToCoords?: boolean,
  enableAutomaticScroll?: boolean,
  extraHeight?: number,
  extraScrollHeight?: number,
  keyboardOpeningTime?: number,
  onScroll?: Function,
  update?: Function,
  contentContainerStyle?: any,
  enableOnAndroid?: boolean,
  innerRef?: Function,
  ...keyboardAwareHOCTypeEvents
}
export type KeyboardAwareHOCState = {
  keyboardSpace: number
}

export type ElementLayout = {
  x: number,
  y: number,
  width: number,
  height: number
}

export type ContentOffset = {
  x: number,
  y: number
}

export type ScrollPosition = {
  x: number,
  y: number,
  animated: boolean
}

export type ScrollIntoViewOptions = ?{
  getScrollPosition?: (
    parentLayout: ElementLayout,
    childLayout: ElementLayout,
    contentOffset: ContentOffset
  ) => ScrollPosition
}

function getDisplayName(WrappedComponent: React$Component) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

function listenToKeyboardEvents(ScrollableComponent: React$Component) {
  return class extends React.Component<
    KeyboardAwareHOCProps,
    KeyboardAwareHOCState
    > implements KeyboardAwareInterface {
    _rnkasv_keyboardView: any
    keyboardWillShowEvent: ?Function
    keyboardWillHideEvent: ?Function
    position: ContentOffset
    defaultResetScrollToCoords: ?{ x: number, y: number }
    resetCoords: ?{ x: number, y: number }
    mountedComponent: boolean
    handleOnScroll: Function
    state: KeyboardAwareHOCState
    static displayName = `KeyboardAware${getDisplayName(ScrollableComponent)}`

    static propTypes = {
      viewIsInsideTabBar: PropTypes.bool,
      resetScrollToCoords: PropTypes.shape({
        x: PropTypes.number.isRequired,
        y: PropTypes.number.isRequired
      }),
      enableResetScrollToCoords: PropTypes.bool,
      enableAutomaticScroll: PropTypes.bool,
      extraHeight: PropTypes.number,
      extraScrollHeight: PropTypes.number,
      keyboardOpeningTime: PropTypes.number,
      onScroll: PropTypes.func,
      update: PropTypes.func,
      contentContainerStyle: PropTypes.any,
      enableOnAndroid: PropTypes.bool,
      innerRef: PropTypes.func,
      ...keyboardEventPropTypes
    }

    static defaultProps = {
      enableAutomaticScroll: true,
      extraHeight: _KAM_EXTRA_HEIGHT,
      extraScrollHeight: 0,
      enableResetScrollToCoords: true,
      keyboardOpeningTime: _KAM_KEYBOARD_OPENING_TIME,
      viewIsInsideTabBar: false
    }

    constructor(props: KeyboardAwareHOCProps) {
      super(props)
      this.keyboardWillShowEvent = undefined
      this.keyboardWillHideEvent = undefined
      this.callbacks = {}
      this.position = { x: 0, y: 0 }
      this.defaultResetScrollToCoords = null
      const keyboardSpace: number = props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.state = { keyboardSpace }
    }

    componentDidMount() {
      this.mountedComponent = true
      // Keyboard events
      if (Platform.OS === 'ios') {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardWillShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardWillHide',
          this._resetKeyboardSpace
        )
      } else if (Platform.OS === 'android' && this.props.enableOnAndroid) {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardDidShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardDidHide',
          this._resetKeyboardSpace
        )
      }

      supportedKeyboardEvents.forEach((eventName: string) => {
        const callbackName = keyboardEventToCallbackName(eventName)
        if (this.props[callbackName]) {
          this.callbacks[eventName] = Keyboard.addListener(eventName, this.props[callbackName])
        }
      })
    }

    componentWillReceiveProps(nextProps: KeyboardAwareHOCProps) {
      if (nextProps.viewIsInsideTabBar !== this.props.viewIsInsideTabBar) {
        const keyboardSpace: number = nextProps.viewIsInsideTabBar
          ? _KAM_DEFAULT_TAB_BAR_HEIGHT
          : 0
        if (this.state.keyboardSpace !== keyboardSpace) {
          this.setState({ keyboardSpace })
        }
      }
    }

    componentWillUnmount() {
      this.mountedComponent = false
      this.keyboardWillShowEvent && this.keyboardWillShowEvent.remove()
      this.keyboardWillHideEvent && this.keyboardWillHideEvent.remove()
      Object.values(this.callbacks).forEach((callback: Object) => callback.remove())
    }

    getScrollResponder = () => {
      return (
        this._rnkasv_keyboardView &&
        this._rnkasv_keyboardView.getScrollResponder()
      )
    }

    scrollToPosition = (x: number, y: number, animated: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollTo({ x, y, animated })
    }

    scrollToEnd = (animated?: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollToEnd({ animated })
    }

    scrollForExtraHeightOnAndroid = (extraHeight: number) => {
      this.scrollToPosition(0, this.position.y + extraHeight, true)
    }

    /**
     * @param keyboardOpeningTime: takes a different keyboardOpeningTime in consideration.
     * @param extraHeight: takes an extra height in consideration.
     */
    scrollToFocusedInput = (
      reactNode: any,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight || 0
      }
      if (keyboardOpeningTime === undefined) {
        keyboardOpeningTime = this.props.keyboardOpeningTime || 0
      }
      setTimeout(() => {
        if (!this.mountedComponent) {
          return
        }
        const responder = this.getScrollResponder()
        responder &&
          responder.scrollResponderScrollNativeHandleToKeyboard(
            reactNode,
            extraHeight,
            true
          )
      }, keyboardOpeningTime)
    }

    scrollIntoView = async (
      element: React.Element<*>,
      options: ScrollIntoViewOptions = {}
    ) => {
      if (!this._rnkasv_keyboardView || !element) {
        return
      }

      const [
        parentLayout,
        childLayout
      ] = await Promise.all([
        this._measureElement(this._rnkasv_keyboardView),
        this._measureElement(element)
      ])

      const getScrollPosition = options.getScrollPosition || this._defaultGetScrollPosition
      const { x, y, animated } = getScrollPosition(parentLayout, childLayout, this.position)
      this.scrollToPosition(x, y, animated)
    }

    _defaultGetScrollPosition = (
      parentLayout: ElementLayout,
      childLayout: ElementLayout,
      contentOffset: ContentOffset
    ): ScrollPosition => {
      return {
        x: 0,
        y: Math.max(0, childLayout.y - parentLayout.y + contentOffset.y),
        animated: true,
      }
    }

    _measureElement = (element: React.Element<*>): Promise<ElementLayout> => {
      const node = findNodeHandle(element)
      return new Promise((resolve: (ElementLayout) => void) => {
        UIManager.measureInWindow(node, (x: number, y: number, width: number, height: number) => {
          resolve({ x, y, width, height })
        })
      })
    }

    // Keyboard actions
    _updateKeyboardSpace = (frames: Object) => {
      // Automatically scroll to focused TextInput
      if (this.props.enableAutomaticScroll) {
        let keyboardSpace: number = frames.endCoordinates.height + this.props.extraScrollHeight
        if (this.props.viewIsInsideTabBar) {
          keyboardSpace -= _KAM_DEFAULT_TAB_BAR_HEIGHT
        }
        this.setState({ keyboardSpace })
        const currentlyFocusedField = TextInput.State.currentlyFocusedField()
        const responder = this.getScrollResponder()
        if (!currentlyFocusedField || !responder) {
          return
        }
        UIManager.viewIsDescendantOf(
          currentlyFocusedField,
          responder.getInnerViewNode(),
          (isAncestor: boolean) => {
            if (isAncestor) {
              // Check if the TextInput will be hidden by the keyboard
              UIManager.measureInWindow(
                currentlyFocusedField,
                (x: number, y: number, width: number, height: number) => {
                  const textInputBottomPosition = y + height
                  const keyboardPosition = frames.endCoordinates.screenY
                  const totalExtraHeight =
                    this.props.extraScrollHeight + this.props.extraHeight
                  if (Platform.OS === 'ios') {
                    if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this._scrollToFocusedInputWithNodeHandle(
                        currentlyFocusedField
                      )
                    }
                  } else {
                    // On android, the system would scroll the text input just
                    // above the keyboard so we just neet to scroll the extra
                    // height part
                    if (textInputBottomPosition > keyboardPosition) {
                      // Since the system already scrolled the whole view up
                      // we should reduce that amount
                      keyboardSpace =
                        keyboardSpace -
                        (textInputBottomPosition - keyboardPosition)
                      this.setState({ keyboardSpace })
                      this.scrollForExtraHeightOnAndroid(totalExtraHeight)
                    } else if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this.scrollForExtraHeightOnAndroid(
                        totalExtraHeight -
                          (keyboardPosition - textInputBottomPosition)
                      )
                    }
                  }
                }
              )
            }
          }
        )
      }
      if (!this.resetCoords) {
        if (!this.defaultResetScrollToCoords) {
          this.defaultResetScrollToCoords = this.position
        }
      }
    }

    _resetKeyboardSpace = () => {
      const keyboardSpace: number = this.props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT + this.props.extraScrollHeight || 0
        : this.props.extraScrollHeight || 0
      this.setState({ keyboardSpace })
      // Reset scroll position after keyboard dismissal
      if (this.props.enableResetScrollToCoords === false) {
        this.defaultResetScrollToCoords = null
        return
      } else if (this.resetCoords) {
        this.scrollToPosition(this.resetCoords.x, this.resetCoords.y, true)
      } else {
        if (this.defaultResetScrollToCoords) {
          this.scrollToPosition(
            this.defaultResetScrollToCoords.x,
            this.defaultResetScrollToCoords.y,
            true
          )
          this.defaultResetScrollToCoords = null
        } else {
          this.scrollToPosition(0, 0, true)
        }
      }
    }

    _scrollToFocusedInputWithNodeHandle = (
      nodeID: number,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight
      }
      const reactNode = ReactNative.findNodeHandle(nodeID)
      this.scrollToFocusedInput(
        reactNode,
        extraHeight + this.props.extraScrollHeight,
        keyboardOpeningTime !== undefined
          ? keyboardOpeningTime
          : this.props.keyboardOpeningTime || 0
      )
    }

    _handleOnScroll = (
      e: SyntheticEvent<*> & { nativeEvent: { contentOffset: number } }
    ) => {
      this.position = e.nativeEvent.contentOffset
    }

    _handleRef = (ref: React.Component<*>) => {
      this._rnkasv_keyboardView = ref
      if (this.props.innerRef) {
        this.props.innerRef(this._rnkasv_keyboardView)
      }
    }

    update = () => {
      const currentlyFocusedField = TextInput.State.currentlyFocusedField()
      const responder = this.getScrollResponder()

      if (!currentlyFocusedField || !responder) {
        return
      }

      this._scrollToFocusedInputWithNodeHandle(
        currentlyFocusedField
      )
    }

    render() {
      const { enableOnAndroid, contentContainerStyle, onScroll } = this.props
      let newContentContainerStyle
      if (Platform.OS === 'android' && enableOnAndroid) {
        newContentContainerStyle = [].concat(contentContainerStyle).concat({
          paddingBottom:
            ((contentContainerStyle || {}).paddingBottom || 0) +
            this.state.keyboardSpace
        })
      }
      return (
        <ScrollableComponent
          ref={this._handleRef}
          keyboardDismissMode='interactive'
          contentInset={{ bottom: this.state.keyboardSpace }}
          automaticallyAdjustContentInsets={false}
          showsVerticalScrollIndicator={true}
          scrollEventThrottle={1}
          {...this.props}
          contentContainerStyle={
            newContentContainerStyle || contentContainerStyle
          }
          keyboardSpace={this.state.keyboardSpace}
          getScrollResponder={this.getScrollResponder}
          scrollToPosition={this.scrollToPosition}
          scrollToEnd={this.scrollToEnd}
          scrollForExtraHeightOnAndroid={this.scrollForExtraHeightOnAndroid}
          scrollToFocusedInput={this.scrollToFocusedInput}
          scrollIntoView={this.scrollIntoView}
          resetKeyboardSpace={this._resetKeyboardSpace}
          handleOnScroll={this._handleOnScroll}
          update={this.update}
          onScroll={Animated.forkEvent(onScroll,this._handleOnScroll)}
        />
      )
    }
  }
}

export default listenToKeyboardEvents
<commit_msg>_handleRef: support for Animated wrappers (#274)<commit_after>/* @flow */

import React from 'react'
import PropTypes from 'prop-types'
import ReactNative, {
  Keyboard,
  Platform,
  UIManager,
  TextInput,
  findNodeHandle,
  Animated
} from 'react-native'
import { isIphoneX } from 'react-native-iphone-x-helper'
import type { KeyboardAwareInterface } from './KeyboardAwareInterface'

const _KAM_DEFAULT_TAB_BAR_HEIGHT: number = isIphoneX() ? 83 : 49
const _KAM_KEYBOARD_OPENING_TIME: number = 250
const _KAM_EXTRA_HEIGHT: number = 75

const supportedKeyboardEvents = [
  'keyboardWillShow',
  'keyboardDidShow',
  'keyboardWillHide',
  'keyboardDidHide',
  'keyboardWillChangeFrame',
  'keyboardDidChangeFrame',
]
const keyboardEventToCallbackName = (eventName: string) => (
  'on' + eventName[0].toUpperCase() + eventName.substring(1)
)
const keyboardEventPropTypes = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => (
    { ...acc, [keyboardEventToCallbackName(eventName)]: PropTypes.func }
  ), {}
)
const keyboardAwareHOCTypeEvents = supportedKeyboardEvents.reduce(
  (acc: Object, eventName: string) => (
    { ...acc, [keyboardEventToCallbackName(eventName)]: Function }
  ), {}
)

export type KeyboardAwareHOCProps = {
  viewIsInsideTabBar?: boolean,
  resetScrollToCoords?: {
    x: number,
    y: number
  },
  enableResetScrollToCoords?: boolean,
  enableAutomaticScroll?: boolean,
  extraHeight?: number,
  extraScrollHeight?: number,
  keyboardOpeningTime?: number,
  onScroll?: Function,
  update?: Function,
  contentContainerStyle?: any,
  enableOnAndroid?: boolean,
  innerRef?: Function,
  ...keyboardAwareHOCTypeEvents
}
export type KeyboardAwareHOCState = {
  keyboardSpace: number
}

export type ElementLayout = {
  x: number,
  y: number,
  width: number,
  height: number
}

export type ContentOffset = {
  x: number,
  y: number
}

export type ScrollPosition = {
  x: number,
  y: number,
  animated: boolean
}

export type ScrollIntoViewOptions = ?{
  getScrollPosition?: (
    parentLayout: ElementLayout,
    childLayout: ElementLayout,
    contentOffset: ContentOffset
  ) => ScrollPosition
}

function getDisplayName(WrappedComponent: React$Component) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

function listenToKeyboardEvents(ScrollableComponent: React$Component) {
  return class extends React.Component<
    KeyboardAwareHOCProps,
    KeyboardAwareHOCState
    > implements KeyboardAwareInterface {
    _rnkasv_keyboardView: any
    keyboardWillShowEvent: ?Function
    keyboardWillHideEvent: ?Function
    position: ContentOffset
    defaultResetScrollToCoords: ?{ x: number, y: number }
    resetCoords: ?{ x: number, y: number }
    mountedComponent: boolean
    handleOnScroll: Function
    state: KeyboardAwareHOCState
    static displayName = `KeyboardAware${getDisplayName(ScrollableComponent)}`

    static propTypes = {
      viewIsInsideTabBar: PropTypes.bool,
      resetScrollToCoords: PropTypes.shape({
        x: PropTypes.number.isRequired,
        y: PropTypes.number.isRequired
      }),
      enableResetScrollToCoords: PropTypes.bool,
      enableAutomaticScroll: PropTypes.bool,
      extraHeight: PropTypes.number,
      extraScrollHeight: PropTypes.number,
      keyboardOpeningTime: PropTypes.number,
      onScroll: PropTypes.func,
      update: PropTypes.func,
      contentContainerStyle: PropTypes.any,
      enableOnAndroid: PropTypes.bool,
      innerRef: PropTypes.func,
      ...keyboardEventPropTypes
    }

    static defaultProps = {
      enableAutomaticScroll: true,
      extraHeight: _KAM_EXTRA_HEIGHT,
      extraScrollHeight: 0,
      enableResetScrollToCoords: true,
      keyboardOpeningTime: _KAM_KEYBOARD_OPENING_TIME,
      viewIsInsideTabBar: false
    }

    constructor(props: KeyboardAwareHOCProps) {
      super(props)
      this.keyboardWillShowEvent = undefined
      this.keyboardWillHideEvent = undefined
      this.callbacks = {}
      this.position = { x: 0, y: 0 }
      this.defaultResetScrollToCoords = null
      const keyboardSpace: number = props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT
        : 0
      this.state = { keyboardSpace }
    }

    componentDidMount() {
      this.mountedComponent = true
      // Keyboard events
      if (Platform.OS === 'ios') {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardWillShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardWillHide',
          this._resetKeyboardSpace
        )
      } else if (Platform.OS === 'android' && this.props.enableOnAndroid) {
        this.keyboardWillShowEvent = Keyboard.addListener(
          'keyboardDidShow',
          this._updateKeyboardSpace
        )
        this.keyboardWillHideEvent = Keyboard.addListener(
          'keyboardDidHide',
          this._resetKeyboardSpace
        )
      }

      supportedKeyboardEvents.forEach((eventName: string) => {
        const callbackName = keyboardEventToCallbackName(eventName)
        if (this.props[callbackName]) {
          this.callbacks[eventName] = Keyboard.addListener(eventName, this.props[callbackName])
        }
      })
    }

    componentWillReceiveProps(nextProps: KeyboardAwareHOCProps) {
      if (nextProps.viewIsInsideTabBar !== this.props.viewIsInsideTabBar) {
        const keyboardSpace: number = nextProps.viewIsInsideTabBar
          ? _KAM_DEFAULT_TAB_BAR_HEIGHT
          : 0
        if (this.state.keyboardSpace !== keyboardSpace) {
          this.setState({ keyboardSpace })
        }
      }
    }

    componentWillUnmount() {
      this.mountedComponent = false
      this.keyboardWillShowEvent && this.keyboardWillShowEvent.remove()
      this.keyboardWillHideEvent && this.keyboardWillHideEvent.remove()
      Object.values(this.callbacks).forEach((callback: Object) => callback.remove())
    }

    getScrollResponder = () => {
      return (
        this._rnkasv_keyboardView &&
        this._rnkasv_keyboardView.getScrollResponder()
      )
    }

    scrollToPosition = (x: number, y: number, animated: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollTo({ x, y, animated })
    }

    scrollToEnd = (animated?: boolean = true) => {
      const responder = this.getScrollResponder()
      responder && responder.scrollResponderScrollToEnd({ animated })
    }

    scrollForExtraHeightOnAndroid = (extraHeight: number) => {
      this.scrollToPosition(0, this.position.y + extraHeight, true)
    }

    /**
     * @param keyboardOpeningTime: takes a different keyboardOpeningTime in consideration.
     * @param extraHeight: takes an extra height in consideration.
     */
    scrollToFocusedInput = (
      reactNode: any,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight || 0
      }
      if (keyboardOpeningTime === undefined) {
        keyboardOpeningTime = this.props.keyboardOpeningTime || 0
      }
      setTimeout(() => {
        if (!this.mountedComponent) {
          return
        }
        const responder = this.getScrollResponder()
        responder &&
          responder.scrollResponderScrollNativeHandleToKeyboard(
            reactNode,
            extraHeight,
            true
          )
      }, keyboardOpeningTime)
    }

    scrollIntoView = async (
      element: React.Element<*>,
      options: ScrollIntoViewOptions = {}
    ) => {
      if (!this._rnkasv_keyboardView || !element) {
        return
      }

      const [
        parentLayout,
        childLayout
      ] = await Promise.all([
        this._measureElement(this._rnkasv_keyboardView),
        this._measureElement(element)
      ])

      const getScrollPosition = options.getScrollPosition || this._defaultGetScrollPosition
      const { x, y, animated } = getScrollPosition(parentLayout, childLayout, this.position)
      this.scrollToPosition(x, y, animated)
    }

    _defaultGetScrollPosition = (
      parentLayout: ElementLayout,
      childLayout: ElementLayout,
      contentOffset: ContentOffset
    ): ScrollPosition => {
      return {
        x: 0,
        y: Math.max(0, childLayout.y - parentLayout.y + contentOffset.y),
        animated: true,
      }
    }

    _measureElement = (element: React.Element<*>): Promise<ElementLayout> => {
      const node = findNodeHandle(element)
      return new Promise((resolve: (ElementLayout) => void) => {
        UIManager.measureInWindow(node, (x: number, y: number, width: number, height: number) => {
          resolve({ x, y, width, height })
        })
      })
    }

    // Keyboard actions
    _updateKeyboardSpace = (frames: Object) => {
      // Automatically scroll to focused TextInput
      if (this.props.enableAutomaticScroll) {
        let keyboardSpace: number = frames.endCoordinates.height + this.props.extraScrollHeight
        if (this.props.viewIsInsideTabBar) {
          keyboardSpace -= _KAM_DEFAULT_TAB_BAR_HEIGHT
        }
        this.setState({ keyboardSpace })
        const currentlyFocusedField = TextInput.State.currentlyFocusedField()
        const responder = this.getScrollResponder()
        if (!currentlyFocusedField || !responder) {
          return
        }
        UIManager.viewIsDescendantOf(
          currentlyFocusedField,
          responder.getInnerViewNode(),
          (isAncestor: boolean) => {
            if (isAncestor) {
              // Check if the TextInput will be hidden by the keyboard
              UIManager.measureInWindow(
                currentlyFocusedField,
                (x: number, y: number, width: number, height: number) => {
                  const textInputBottomPosition = y + height
                  const keyboardPosition = frames.endCoordinates.screenY
                  const totalExtraHeight =
                    this.props.extraScrollHeight + this.props.extraHeight
                  if (Platform.OS === 'ios') {
                    if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this._scrollToFocusedInputWithNodeHandle(
                        currentlyFocusedField
                      )
                    }
                  } else {
                    // On android, the system would scroll the text input just
                    // above the keyboard so we just neet to scroll the extra
                    // height part
                    if (textInputBottomPosition > keyboardPosition) {
                      // Since the system already scrolled the whole view up
                      // we should reduce that amount
                      keyboardSpace =
                        keyboardSpace -
                        (textInputBottomPosition - keyboardPosition)
                      this.setState({ keyboardSpace })
                      this.scrollForExtraHeightOnAndroid(totalExtraHeight)
                    } else if (
                      textInputBottomPosition >
                      keyboardPosition - totalExtraHeight
                    ) {
                      this.scrollForExtraHeightOnAndroid(
                        totalExtraHeight -
                          (keyboardPosition - textInputBottomPosition)
                      )
                    }
                  }
                }
              )
            }
          }
        )
      }
      if (!this.resetCoords) {
        if (!this.defaultResetScrollToCoords) {
          this.defaultResetScrollToCoords = this.position
        }
      }
    }

    _resetKeyboardSpace = () => {
      const keyboardSpace: number = this.props.viewIsInsideTabBar
        ? _KAM_DEFAULT_TAB_BAR_HEIGHT + this.props.extraScrollHeight || 0
        : this.props.extraScrollHeight || 0
      this.setState({ keyboardSpace })
      // Reset scroll position after keyboard dismissal
      if (this.props.enableResetScrollToCoords === false) {
        this.defaultResetScrollToCoords = null
        return
      } else if (this.resetCoords) {
        this.scrollToPosition(this.resetCoords.x, this.resetCoords.y, true)
      } else {
        if (this.defaultResetScrollToCoords) {
          this.scrollToPosition(
            this.defaultResetScrollToCoords.x,
            this.defaultResetScrollToCoords.y,
            true
          )
          this.defaultResetScrollToCoords = null
        } else {
          this.scrollToPosition(0, 0, true)
        }
      }
    }

    _scrollToFocusedInputWithNodeHandle = (
      nodeID: number,
      extraHeight?: number,
      keyboardOpeningTime?: number
    ) => {
      if (extraHeight === undefined) {
        extraHeight = this.props.extraHeight
      }
      const reactNode = ReactNative.findNodeHandle(nodeID)
      this.scrollToFocusedInput(
        reactNode,
        extraHeight + this.props.extraScrollHeight,
        keyboardOpeningTime !== undefined
          ? keyboardOpeningTime
          : this.props.keyboardOpeningTime || 0
      )
    }

    _handleOnScroll = (
      e: SyntheticEvent<*> & { nativeEvent: { contentOffset: number } }
    ) => {
      this.position = e.nativeEvent.contentOffset
    }

    _handleRef = (ref: React.Component<*>) => {
      this._rnkasv_keyboardView = ref.getNode ? ref.getNode() : ref
      if (this.props.innerRef) {
        this.props.innerRef(this._rnkasv_keyboardView)
      }
    }

    update = () => {
      const currentlyFocusedField = TextInput.State.currentlyFocusedField()
      const responder = this.getScrollResponder()

      if (!currentlyFocusedField || !responder) {
        return
      }

      this._scrollToFocusedInputWithNodeHandle(
        currentlyFocusedField
      )
    }

    render() {
      const { enableOnAndroid, contentContainerStyle, onScroll } = this.props
      let newContentContainerStyle
      if (Platform.OS === 'android' && enableOnAndroid) {
        newContentContainerStyle = [].concat(contentContainerStyle).concat({
          paddingBottom:
            ((contentContainerStyle || {}).paddingBottom || 0) +
            this.state.keyboardSpace
        })
      }
      return (
        <ScrollableComponent
          ref={this._handleRef}
          keyboardDismissMode='interactive'
          contentInset={{ bottom: this.state.keyboardSpace }}
          automaticallyAdjustContentInsets={false}
          showsVerticalScrollIndicator={true}
          scrollEventThrottle={1}
          {...this.props}
          contentContainerStyle={
            newContentContainerStyle || contentContainerStyle
          }
          keyboardSpace={this.state.keyboardSpace}
          getScrollResponder={this.getScrollResponder}
          scrollToPosition={this.scrollToPosition}
          scrollToEnd={this.scrollToEnd}
          scrollForExtraHeightOnAndroid={this.scrollForExtraHeightOnAndroid}
          scrollToFocusedInput={this.scrollToFocusedInput}
          scrollIntoView={this.scrollIntoView}
          resetKeyboardSpace={this._resetKeyboardSpace}
          handleOnScroll={this._handleOnScroll}
          update={this.update}
          onScroll={Animated.forkEvent(onScroll,this._handleOnScroll)}
        />
      )
    }
  }
}

export default listenToKeyboardEvents
<|endoftext|>


<commit_before>QUnit.module('templateParser - error', {
  before: function() {
    this.parse = function() {
      this.result = this.parser.parse(this.source);
    };
  },
  beforeEach: function() {
    this.parser = smodules.templateParser();
    this.source = '';
    this.result = null;
  },
});

QUnit.test('normal block - error - 1', function(assert) {
  this.source = '
{left} is ok, only { is forbidden.
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('normal block - error - 2', function(assert) {
  this.source = '
 } is forbidden.
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('literal block - error - only open tag', function(assert) {
  this.source = '
{literal}
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('literal block - error - only close tag', function(assert) {
  this.source = '
{/literal}
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - space between $ and property name', function(assert) {
  this.source = '{ $ foo } has space between $ and property name.';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - dot between $ and property name', function(assert) {
  this.source = '{ $.foo }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - dot after property name', function(assert) {
  this.source = '{ $foo. }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - continuous dots', function(assert) {
  this.source = '{ $foo..bar }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - filter name has space', function(assert) {
  this.source = '{ $foo | invalid filter name }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - filter name has symbol', function(assert) {
  this.source = '{ $foo | filter! }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - no pipe', function(assert) {
  this.source = '{ $foo pipeNotFound }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - no filter args after colon', function(assert) {
  this.source = '{ $foo | filter : }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - NULL', function(assert) {
  this.source = '{ $foo | filter : NULL }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - TRUE', function(assert) {
  this.source = '{ $foo | filter : TRUE }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - FALSE', function(assert) {
  this.source = '{ $foo | filter : FALSE }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote 1', function(assert) {
  this.source = '{ $foo | filter : \'test }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote 2', function(assert) {
  this.source = '{ $foo | filter : test\' }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote char 1', function(assert) {
  this.source = '{ $foo | filter : "test\' }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote char 2', function(assert) {
  this.source = '{ $foo | filter : \'test" }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 1', function(assert) {
  this.source = '{if $foo}
hoge
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 2', function(assert) {
  this.source = '{elseif $foo}
hoge
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 3', function(assert) {
  this.source = '{else}
hoge
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 4', function(assert) {
  this.source = '{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});
QUnit.test('if block - if elseif else - error 5', function(assert) {
  this.source = '{if $foo}
foo
{if $bar}
bar
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - no space 1', function(assert) {
  this.source = '{if$foo }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - no space 2', function(assert) {
  this.source = '{if $foo }
foo
{elseif$bar}
bar
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - round bracket balance', function(assert) {
  this.source = '{if ($foo) and $bar) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - lack of endRoundBracket', function(assert) {
  this.source = '{if ( $foo }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - lack of roundBracket', function(assert) {
  this.source = '{if ( $foo ) ) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> endRoundBracket', function(assert) {
  this.source = '{if () }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> comp', function(assert) {
  this.source = '{if ( === $foo )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> andor', function(assert) {
  this.source = '{if ( and $foo )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> roundBracket', function(assert) {
  this.source = '{if ( $foo ) ( $bar )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> value', function(assert) {
  this.source = '{if ( $foo ) 10 === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> var', function(assert) {
  this.source = '{if ( $foo ) $bar gte 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> comp', function(assert) {
  this.source = '{if ( $foo ) === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> roundBracket', function(assert) {
  this.source = '{if 10 ( $foo === 1 )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> value', function(assert) {
  this.source = '{if 10 20}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> var', function(assert) {
  this.source = '{if 10 $foo}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> roundBracket', function(assert) {
  this.source = '{if $foo ( $bar === 1 )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> value', function(assert) {
  this.source = '{if $foo 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> var', function(assert) {
  this.source = '{if $foo $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> roundBracket', function(assert) {
  this.source = '{if $foo lte ( $bar ) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> endRoundBracket', function(assert) {
  this.source = '{if ( $foo gte ) $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> comp', function(assert) {
  this.source = '{if $foo === === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> andor', function(assert) {
  this.source = '{if $foo !== or $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> value -> comp', function(assert) {
  this.source = '{if 10 === 10 === 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> var -> comp', function(assert) {
  this.source = '{if $foo gt $bar gt $baz}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> endRoundBracket', function(assert) {
  this.source = '{if ( $foo or ) $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> comp', function(assert) {
  this.source = '{if $foo or === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> andor', function(assert) {
  this.source = '{if $foo or or $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of index argument', function(assert) {
  this.source = '{for , $item in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of comma', function(assert) {
  this.source = '{for $idx $item in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of value argument', function(assert) {
  this.source = '{for $idx , in $items}
{$idx}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of in', function(assert) {
  this.source = '{for $idx , $item $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of haystack', function(assert) {
  this.source = '{for $idx , $item in}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 1', function(assert) {
  this.source = '{for $idx , $item , $foo in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 2', function(assert) {

  this.source = '{for $idx , $item in $items1 , $items2}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 3', function(assert) {

  this.source = '{for $idx , $item in $items1 $items2}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});
<commit_msg>[QUnit] Add tests for holder block<commit_after>QUnit.module('templateParser - error', {
  before: function() {
    this.parse = function() {
      this.result = this.parser.parse(this.source);
    };
  },
  beforeEach: function() {
    this.parser = smodules.templateParser();
    this.source = '';
    this.result = null;
  },
});

QUnit.test('normal block - error - 1', function(assert) {
  this.source = '
{left} is ok, only { is forbidden.
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('normal block - error - 2', function(assert) {
  this.source = '
 } is forbidden.
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('literal block - error - only open tag', function(assert) {
  this.source = '
{literal}
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('literal block - error - only close tag', function(assert) {
  this.source = '
{/literal}
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - space between $ and property name', function(assert) {
  this.source = '{ $ foo } has space between $ and property name.';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - dot between $ and property name', function(assert) {
  this.source = '{ $.foo }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - dot after property name', function(assert) {
  this.source = '{ $foo. }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - no filters - error - continuous dots', function(assert) {
  this.source = '{ $foo..bar }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - filter name has space', function(assert) {
  this.source = '{ $foo | invalid filter name }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - filter name has symbol', function(assert) {
  this.source = '{ $foo | filter! }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - no pipe', function(assert) {
  this.source = '{ $foo pipeNotFound }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - no filter name before colon', function(assert) {
  this.source = '{ $foo | : 1 }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - no filter args after colon', function(assert) {
  this.source = '{ $foo | filter : }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter - error - colon only', function(assert) {
  this.source = '{ $foo | : }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - NULL', function(assert) {
  this.source = '{ $foo | filter : NULL }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - TRUE', function(assert) {
  this.source = '{ $foo | filter : TRUE }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - error - FALSE', function(assert) {
  this.source = '{ $foo | filter : FALSE }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote 1', function(assert) {
  this.source = '{ $foo | filter : \'test }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote 2', function(assert) {
  this.source = '{ $foo | filter : test\' }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote char 1', function(assert) {
  this.source = '{ $foo | filter : "test\' }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('holder block - filter with args - string - error - quote char 2', function(assert) {
  this.source = '{ $foo | filter : \'test" }';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 1', function(assert) {
  this.source = '{if $foo}
hoge
';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 2', function(assert) {
  this.source = '{elseif $foo}
hoge
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 3', function(assert) {
  this.source = '{else}
hoge
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - if elseif else - error 4', function(assert) {
  this.source = '{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});
QUnit.test('if block - if elseif else - error 5', function(assert) {
  this.source = '{if $foo}
foo
{if $bar}
bar
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - no space 1', function(assert) {
  this.source = '{if$foo }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - no space 2', function(assert) {
  this.source = '{if $foo }
foo
{elseif$bar}
bar
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - round bracket balance', function(assert) {
  this.source = '{if ($foo) and $bar) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - lack of endRoundBracket', function(assert) {
  this.source = '{if ( $foo }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - lack of roundBracket', function(assert) {
  this.source = '{if ( $foo ) ) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> endRoundBracket', function(assert) {
  this.source = '{if () }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> comp', function(assert) {
  this.source = '{if ( === $foo )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - roundBracket -> andor', function(assert) {
  this.source = '{if ( and $foo )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> roundBracket', function(assert) {
  this.source = '{if ( $foo ) ( $bar )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> value', function(assert) {
  this.source = '{if ( $foo ) 10 === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> var', function(assert) {
  this.source = '{if ( $foo ) $bar gte 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - endRoundBracket -> comp', function(assert) {
  this.source = '{if ( $foo ) === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> roundBracket', function(assert) {
  this.source = '{if 10 ( $foo === 1 )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> value', function(assert) {
  this.source = '{if 10 20}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - value -> var', function(assert) {
  this.source = '{if 10 $foo}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> roundBracket', function(assert) {
  this.source = '{if $foo ( $bar === 1 )}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> value', function(assert) {
  this.source = '{if $foo 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - var -> var', function(assert) {
  this.source = '{if $foo $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> roundBracket', function(assert) {
  this.source = '{if $foo lte ( $bar ) }
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> endRoundBracket', function(assert) {
  this.source = '{if ( $foo gte ) $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> comp', function(assert) {
  this.source = '{if $foo === === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> andor', function(assert) {
  this.source = '{if $foo !== or $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> value -> comp', function(assert) {
  this.source = '{if 10 === 10 === 10}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - comp -> var -> comp', function(assert) {
  this.source = '{if $foo gt $bar gt $baz}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> endRoundBracket', function(assert) {
  this.source = '{if ( $foo or ) $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> comp', function(assert) {
  this.source = '{if $foo or === $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('if block - conditions - error - andor -> andor', function(assert) {
  this.source = '{if $foo or or $bar}
ok
{/if}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of index argument', function(assert) {
  this.source = '{for , $item in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of comma', function(assert) {
  this.source = '{for $idx $item in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of value argument', function(assert) {
  this.source = '{for $idx , in $items}
{$idx}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of in', function(assert) {
  this.source = '{for $idx , $item $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - lack of haystack', function(assert) {
  this.source = '{for $idx , $item in}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 1', function(assert) {
  this.source = '{for $idx , $item , $foo in $items}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 2', function(assert) {

  this.source = '{for $idx , $item in $items1 , $items2}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});

QUnit.test('for block - error - too many elements 3', function(assert) {

  this.source = '{for $idx , $item in $items1 $items2}
{$item}
{/for}';

  assert.throws(function() {
    this.parse();
  }, Error);
});
<|endoftext|>


<commit_before>const fs = require("fs");
const url = require("url");
const server = require("http").createServer(function(request, response) {
  const pathname = require("url").parse(request.url, true).pathname;

  let output;
  if (pathname === "/" || pathname === "/index.html"){
    response.writeHead(200, {"Content-Type": "text/html"});
    output = fs.readFileSync("./index.html", "utf-8");
  }
  else if (pathname === "/result.html"){
    response.writeHead(200, {"Content-Type": "text/html"});
    output = fs.readFileSync("./result.html", "utf-8");
  }
  else {
    response.writeHead(404, {"Content-Type": "text/plain"});
    output = "Not found";
  }
  response.end(output);
}).listen(8080);
const io = require("socket.io").listen(server);
const bingo = require("./bingo.js");

let numbers = bingo.shuffled();
let exist = bingo.defaultResult();

io.sockets.on("connection", function (socket) {
  socket.on("connected", function() {
    socket.emit("initialize", {exist});
    conosle.log("connected");
  });
  socket.on("next", function () {
    if (numbers){
      let next = numbers.shift();
      exist[next] = true;
      io.sockets.emit("next", {next});
    }
  });
  socket.on("reset", function () {
    numbers = bingo.shuffled();
    exist = bingo.defaultResult();
    socket.emit("initialize", {exist});
  });
});
<commit_msg>数列が空だったらoverrunを投げる<commit_after>const fs = require("fs");
const url = require("url");
const server = require("http").createServer(function(request, response) {
  const pathname = require("url").parse(request.url, true).pathname;

  let output;
  if (pathname === "/" || pathname === "/index.html"){
    response.writeHead(200, {"Content-Type": "text/html"});
    output = fs.readFileSync("./index.html", "utf-8");
  }
  else if (pathname === "/result.html"){
    response.writeHead(200, {"Content-Type": "text/html"});
    output = fs.readFileSync("./result.html", "utf-8");
  }
  else {
    response.writeHead(404, {"Content-Type": "text/plain"});
    output = "Not found";
  }
  response.end(output);
}).listen(8080);
const io = require("socket.io").listen(server);
const bingo = require("./bingo.js");

let numbers = bingo.shuffled();
let exist = bingo.defaultResult();

io.sockets.on("connection", function (socket) {
  socket.on("connected", function() {
    socket.emit("initialize", {exist});
    conosle.log("connected");
  });
  socket.on("next", function () {
    if (numbers){
      let next = numbers.shift();
      exist[next] = true;
      io.sockets.emit("next", {next});
    }
    else {
      socket.emit("overrun");
    }
  });
  socket.on("reset", function () {
    numbers = bingo.shuffled();
    exist = bingo.defaultResult();
    socket.emit("initialize", {exist});
  });
});
<|endoftext|>