<commit_before>import React, { Component } from "react"
import { connect } from "react-redux"
import {
	Segment, Grid, Header, Form, Modal, Menu,
	Dropdown, Label, Table, Button, Radio, Icon
} from "semantic-ui-react"
import _ from "lodash"

import { requestCatalog } from "../../actions"
import { requestWorlds, requestChangeWorld, requestCreateWorld, requestDeleteWorld } from "../../actions/world"

const DIM_TYPES = "world.DimensionType"
const GEN_TYPES = "world.GeneratorType"
const DIFF_TYPES = "world.difficulty.Difficulty"
const GM_TYPES = "entity.living.player.gamemode.GameMode"
const ITEMS_PER_PAGE = 20

class Worlds extends Component {

	constructor(props) {
		super(props);

		this.state = {
			modal: false,
			page: 0,
			rules: null,
			rulesWorld: null,
			loadOnStartup: true,
			commandsAllowed: true,
			generateBonusChest: true,
			usesMapFeatures: true,
		};

		this.create = this.create.bind(this);
		this.handleChange = this.handleChange.bind(this);
		this.toggleModal = this.toggleModal.bind(this);
		this.saveGameRules = this.saveGameRules.bind(this);
		this.changePage = this.changePage.bind(this);
	}

	componentDidMount() {
		this.props.requestWorlds();
		this.getCatalogValues();

		this.interval = setInterval(this.props.requestWorlds, 5000);
	}

	componentWillUnmount() {
		clearInterval(this.interval);
	}

	handleChange(event, data) {
		let value = null;
		let name = null;

		if (data) {
			name = data.name ? data.name : data.id;
			value = data.value;
		} else {
			const target = event.target;
			value = target.type === 'checkbox' ? target.checked : target.value;
			name = target.name ? target.name : target.id;
		}

		this.setState({
			[name]: value
		});
	}

	getCatalogValues() {
		this.props.requestCatalog(DIM_TYPES);
		this.props.requestCatalog(GEN_TYPES);
		this.props.requestCatalog(DIFF_TYPES);
		this.props.requestCatalog(GM_TYPES);
	}

	create() {
		this.props.requestCreateWorld({
			name: this.state.worldName,
			dimension: this.state.dimension,
			generator: this.state.generator,
			difficulty: this.state.difficulty,
			gameMode: this.state.gameMode,
			seed: this.state.seed,
			loadOnStartup: this.state.loadOnStartup,
			keepSpawnLoaded: this.state.keepSpawnLoaded,
			allowCommands: this.state.commandsAllowed,
			generateBonusChest: this.state.generateBonusChest,
			usesMapFeatures: this.state.usesMapFeatures,
		})
	}

	toggleLoad(world, callback) {
		const newState = !world.loaded;

		this.props.requestChangeWorld(world.uuid, { loaded: newState }, newState ? "Loaded" : "Unloaded")
	}

	showGameRules(world) {
		this.setState({
			modal: true,
			rules: _.map(world.gameRules, (value, name) => ({ name: name, value: value })),
			rulesWorld: world,
		});
	}

	toggleRule(name) {
		const rules = this.state.rules;
		const rule = _.find(rules, { name: name });
		rule.value = rule.value === "true" ? "false" : "true";

		this.setState({
			rules: rules,
		});
	}

	saveGameRules() {
		const world = this.state.rulesWorld;
		const rules = _.mapValues(_.keyBy(this.state.rules, "name"), "value");

		this.props.requestChangeWorld(world.uuid, { gameRules: rules }, "Change game rules for")
		this.toggleModal();
	}

	delete(world) {
		this.props.requestDeleteWorld(world.uuid);
	}

	toggleModal() {
		this.setState({
			modal: !this.state.modal
		});
	}

	changePage(event, page) {
		event.preventDefault();

		this.setState({
			page: page,
		})
	}

	canCreate() {
		return this.state.worldName && this.state.dimension && this.state.generator &&
			this.state.difficulty && this.state.gameMode;
	}

	render() {
		let worlds = _.filter(this.props.worlds, w => true);

		const maxPage = Math.ceil(worlds.length / ITEMS_PER_PAGE);
		const page = Math.min(this.state.page, maxPage - 1);

		worlds = worlds.slice(page * ITEMS_PER_PAGE, (page + 1) * ITEMS_PER_PAGE);

		return (
			<Segment basic>

				<Segment>

						<Icon name="plus" fitted /> Create a world

					<Form loading={this.props.creating}>

						<Form.Group widths="equal">
							<Form.Input
								id="worldName" label="World name" placeholder="World name"
								required onChange={this.handleChange}
							/>

							<Form.Field id="dimension" label="Dimension" control={Dropdown} placeholder="Dimension"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.dimTypes, dim =>
									({ value: dim.id, text: dim.name })
								)}
							/>

							<Form.Field id="generator" label="Generator" control={Dropdown} placeholder="Generator"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.genTypes, gen =>
									({ value: gen.id, text: gen.name })
								)}
							/>
						</Form.Group>

						<Form.Group widths="equal">
							<Form.Field id="difficulty" label="Difficulty" control={Dropdown} placeholder="Difficulty"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.diffTypes, diff =>
									({ value: diff.id, text: diff.name })
								)}
							/>

							<Form.Field id="gameMode" label="Game mode" control={Dropdown} placeholder="Game mode"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.gmTypes, gm =>
									({ value: gm.id, text: gm.name })
								)}
							/>

							<Form.Input id="seed" label="Seed" placeholder="Seed" onChange={this.handleChange} />
						</Form.Group>

						<Form.Group inline>
							Features:
							<Form.Checkbox
								id="loadOnStartup" label="Load on startup"
								checked={this.state.loadOnStartup} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="keepSpawnLoaded" label="Keep spawn loaded"
								checked={this.state.keepSpawnLoaded} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="commandsAllowed" label="Commands allowed"
								checked={this.state.commandsAllowed} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="generateBonusChest" label="Generate bonus chest"
								checked={this.state.generateBonusChest} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="usesMapFeatures" label="Enable map features"
								checked={this.state.usesMapFeatures} onChange={this.handleChange}
							/>
						</Form.Group>

						<Form.Button color="green" onClick={this.create} disabled={!this.canCreate()}>
							Create
						</Form.Button>

					</Form>
				</Segment>

					<Icon name="globe" fitted /> Worlds

Name / UUID

Dimension
Generator

Difficulty
Game Mode

Time
Weather

Seed

Status

Actions

						{_.map(worlds, world =>

{world.name}
{world.uuid}

									{world.dimensionType ? world.dimensionType.name : "-"}

									{world.generatorType ? world.generatorType.name : "-"}

									<Icon name="signal" /> {world.difficulty ? world.difficulty.name : "-"}

									<Icon name="gamepad" /> {world.gameMode ? world.gameMode.name : "-"}

									<Icon name="clock" /> {world.time ? world.time : "-"}

									<Icon name="cloud" /> {world.weather ? world.weather.name : "-"}

									{world.seed}

									{world ?

											{world.loaded ? "Loaded" : "Unloaded"}

									: null}

									<Button
										type="button" color="blue" disabled={world.updating}
										onClick={() => this.showGameRules(world)}
									>
										Game Rules
									</Button>{" "}
									<Button
										type="button" color={world.loaded ? "yellow" : "green"}
										onClick={() => this.toggleLoad(world)} disabled={world.updating}
									>
										{world.loaded ? "Unload " : "Load "}
									</Button>{" "}
									{!world.loaded ?
										<Button
											type="button" color="red" disabled={world.updating}
											onClick={() => this.delete(world)}
										>
											Delete
										</Button>
									: null}
									{" "}
									{world.updating ?
										<Icon name="spinner" loading />
									: null}

						)}

				{ maxPage > 1 ?
					<Menu pagination>
						{ page > 4 ?
							<Menu.Item onClick={e => this.changePage(e, 0)}>
								1
							</Menu.Item>
						: null }
						{ page > 5 ?
							<Menu.Item onClick={e => this.changePage(e, page - 5)}>
								...
							</Menu.Item>
						: null }
						{ _.map(_.range(Math.max(0, page - 4), Math.min(maxPage, page + 5)), p => (
							<Menu.Item key={p} onClick={e => this.changePage(e, p)} active={p === page}>
								{p + 1}
							</Menu.Item>
						))}
						{ page < maxPage - 6 ?
							<Menu.Item onClick={e => this.changePage(e, page + 5)}>
								...
							</Menu.Item>
						: null }
						{ page < maxPage - 5 ?
							<Menu.Item onClick={e => this.changePage(e, maxPage - 1)}>
								{maxPage}
							</Menu.Item>
						: null }
					</Menu>
				: null }

				{this.state.rules ?
					<Modal open={this.state.modal} onClose={this.toggleModal}>
						<Modal.Header>
							Game Rules for '{this.state.rulesWorld.name}'
							({this.state.rulesWorld.dimensionType.name})
						</Modal.Header>
						<Modal.Content>
							<Grid columns={2}>
								<Grid.Column>

Name

Value

											{_.map(_.slice(this.state.rules, 0, this.state.rules.length / 2), rule =>

{rule.name}

														{rule.value === "true" || rule.value === "false" ?
															<Radio
																toggle checked={rule.value === "true"}
																onClick={() => this.toggleRule(rule.name)}
															/>
														: rule.value}

											)}

								</Grid.Column>

								<Grid.Column>

Name

Value

											{_.map(_.slice(this.state.rules, this.state.rules.length / 2), rule =>

{rule.name}

														{rule.value === "true" || rule.value === "false" ?
															<Radio
																toggle checked={rule.value === "true"}
																onClick={() => this.toggleRule(rule.name)}
															/>
														: rule.value}

											)}

								</Grid.Column>

								<Grid.Column>
									<Button color="blue" onClick={this.saveGameRules}>Save</Button>
									<Button onClick={this.toggleModal}>Cancel</Button>
								</Grid.Column>
							</Grid>
						</Modal.Content>
					</Modal>
				: null}

			</Segment>
		)
	}
}

const mapStateToProps = (_state) => {
	const state = _state.world

	return {
		worlds: state.worlds,
		dimTypes: _state.api.types[DIM_TYPES],
		genTypes: _state.api.types[GEN_TYPES],
		diffTypes: _state.api.types[DIFF_TYPES],
		gmTypes: _state.api.types[GM_TYPES],
		creating: state.creating,
	}
}

const mapDispatchToProps = (dispatch) => {
	return {
		requestWorlds: () => dispatch(requestWorlds(true)),
		requestCatalog: type => dispatch(requestCatalog(type)),
		requestChangeWorld: (uuid, data, op) => dispatch(requestChangeWorld(uuid, data, op)),
		requestCreateWorld: data => dispatch(requestCreateWorld(data)),
		requestDeleteWorld: uuid => dispatch(requestDeleteWorld(uuid)),
	}
}

export default connect(mapStateToProps, mapDispatchToProps)(Worlds);
<commit_msg>fix(worlds): Use action buttons for modal<commit_after>import React, { Component } from "react"
import { connect } from "react-redux"
import {
	Segment, Grid, Header, Form, Modal, Menu,
	Dropdown, Label, Table, Button, Radio, Icon
} from "semantic-ui-react"
import _ from "lodash"

import { requestCatalog } from "../../actions"
import { requestWorlds, requestChangeWorld, requestCreateWorld, requestDeleteWorld } from "../../actions/world"

const DIM_TYPES = "world.DimensionType"
const GEN_TYPES = "world.GeneratorType"
const DIFF_TYPES = "world.difficulty.Difficulty"
const GM_TYPES = "entity.living.player.gamemode.GameMode"
const ITEMS_PER_PAGE = 20

class Worlds extends Component {

	constructor(props) {
		super(props);

		this.state = {
			modal: false,
			page: 0,
			rules: null,
			rulesWorld: null,
			loadOnStartup: true,
			commandsAllowed: true,
			generateBonusChest: true,
			usesMapFeatures: true,
		};

		this.create = this.create.bind(this);
		this.handleChange = this.handleChange.bind(this);
		this.toggleModal = this.toggleModal.bind(this);
		this.saveGameRules = this.saveGameRules.bind(this);
		this.changePage = this.changePage.bind(this);
	}

	componentDidMount() {
		this.props.requestWorlds();
		this.getCatalogValues();

		this.interval = setInterval(this.props.requestWorlds, 5000);
	}

	componentWillUnmount() {
		clearInterval(this.interval);
	}

	handleChange(event, data) {
		let value = null;
		let name = null;

		if (data) {
			name = data.name ? data.name : data.id;
			value = data.value;
		} else {
			const target = event.target;
			value = target.type === 'checkbox' ? target.checked : target.value;
			name = target.name ? target.name : target.id;
		}

		this.setState({
			[name]: value
		});
	}

	getCatalogValues() {
		this.props.requestCatalog(DIM_TYPES);
		this.props.requestCatalog(GEN_TYPES);
		this.props.requestCatalog(DIFF_TYPES);
		this.props.requestCatalog(GM_TYPES);
	}

	create() {
		this.props.requestCreateWorld({
			name: this.state.worldName,
			dimension: this.state.dimension,
			generator: this.state.generator,
			difficulty: this.state.difficulty,
			gameMode: this.state.gameMode,
			seed: this.state.seed,
			loadOnStartup: this.state.loadOnStartup,
			keepSpawnLoaded: this.state.keepSpawnLoaded,
			allowCommands: this.state.commandsAllowed,
			generateBonusChest: this.state.generateBonusChest,
			usesMapFeatures: this.state.usesMapFeatures,
		})
	}

	toggleLoad(world, callback) {
		const newState = !world.loaded;

		this.props.requestChangeWorld(world.uuid, { loaded: newState }, newState ? "Loaded" : "Unloaded")
	}

	showGameRules(world) {
		this.setState({
			modal: true,
			rules: _.map(world.gameRules, (value, name) => ({ name: name, value: value })),
			rulesWorld: world,
		});
	}

	toggleRule(name) {
		const rules = this.state.rules;
		const rule = _.find(rules, { name: name });
		rule.value = rule.value === "true" ? "false" : "true";

		this.setState({
			rules: rules,
		});
	}

	saveGameRules() {
		const world = this.state.rulesWorld;
		const rules = _.mapValues(_.keyBy(this.state.rules, "name"), "value");

		this.props.requestChangeWorld(world.uuid, { gameRules: rules }, "Change game rules for")
		this.toggleModal();
	}

	delete(world) {
		this.props.requestDeleteWorld(world.uuid);
	}

	toggleModal() {
		this.setState({
			modal: !this.state.modal
		});
	}

	changePage(event, page) {
		event.preventDefault();

		this.setState({
			page: page,
		})
	}

	canCreate() {
		return this.state.worldName && this.state.dimension && this.state.generator &&
			this.state.difficulty && this.state.gameMode;
	}

	render() {
		let worlds = _.filter(this.props.worlds, w => true);

		const maxPage = Math.ceil(worlds.length / ITEMS_PER_PAGE);
		const page = Math.min(this.state.page, maxPage - 1);

		worlds = worlds.slice(page * ITEMS_PER_PAGE, (page + 1) * ITEMS_PER_PAGE);

		return (
			<Segment basic>

				<Segment>

						<Icon name="plus" fitted /> Create a world

					<Form loading={this.props.creating}>

						<Form.Group widths="equal">
							<Form.Input
								id="worldName" label="World name" placeholder="World name"
								required onChange={this.handleChange}
							/>

							<Form.Field id="dimension" label="Dimension" control={Dropdown} placeholder="Dimension"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.dimTypes, dim =>
									({ value: dim.id, text: dim.name })
								)}
							/>

							<Form.Field id="generator" label="Generator" control={Dropdown} placeholder="Generator"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.genTypes, gen =>
									({ value: gen.id, text: gen.name })
								)}
							/>
						</Form.Group>

						<Form.Group widths="equal">
							<Form.Field id="difficulty" label="Difficulty" control={Dropdown} placeholder="Difficulty"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.diffTypes, diff =>
									({ value: diff.id, text: diff.name })
								)}
							/>

							<Form.Field id="gameMode" label="Game mode" control={Dropdown} placeholder="Game mode"
								required fluid selection search onChange={this.handleChange}
								options={_.map(this.props.gmTypes, gm =>
									({ value: gm.id, text: gm.name })
								)}
							/>

							<Form.Input id="seed" label="Seed" placeholder="Seed" onChange={this.handleChange} />
						</Form.Group>

						<Form.Group inline>
							Features:
							<Form.Checkbox
								id="loadOnStartup" label="Load on startup"
								checked={this.state.loadOnStartup} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="keepSpawnLoaded" label="Keep spawn loaded"
								checked={this.state.keepSpawnLoaded} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="commandsAllowed" label="Commands allowed"
								checked={this.state.commandsAllowed} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="generateBonusChest" label="Generate bonus chest"
								checked={this.state.generateBonusChest} onChange={this.handleChange}
							/>
							<Form.Checkbox
								id="usesMapFeatures" label="Enable map features"
								checked={this.state.usesMapFeatures} onChange={this.handleChange}
							/>
						</Form.Group>

						<Form.Button color="green" onClick={this.create} disabled={!this.canCreate()}>
							Create
						</Form.Button>

					</Form>
				</Segment>

					<Icon name="globe" fitted /> Worlds

Name / UUID

Dimension
Generator

Difficulty
Game Mode

Time
Weather

Seed

Status

Actions

						{_.map(worlds, world =>

{world.name}
{world.uuid}

									{world.dimensionType ? world.dimensionType.name : "-"}

									{world.generatorType ? world.generatorType.name : "-"}

									<Icon name="signal" /> {world.difficulty ? world.difficulty.name : "-"}

									<Icon name="gamepad" /> {world.gameMode ? world.gameMode.name : "-"}

									<Icon name="clock" /> {world.time ? world.time : "-"}

									<Icon name="cloud" /> {world.weather ? world.weather.name : "-"}

									{world.seed}

									{world ?

											{world.loaded ? "Loaded" : "Unloaded"}

									: null}

									<Button
										type="button" color="blue" disabled={world.updating}
										onClick={() => this.showGameRules(world)}
									>
										Game Rules
									</Button>{" "}
									<Button
										type="button" color={world.loaded ? "yellow" : "green"}
										onClick={() => this.toggleLoad(world)} disabled={world.updating}
									>
										{world.loaded ? "Unload " : "Load "}
									</Button>{" "}
									{!world.loaded ?
										<Button
											type="button" color="red" disabled={world.updating}
											onClick={() => this.delete(world)}
										>
											Delete
										</Button>
									: null}
									{" "}
									{world.updating ?
										<Icon name="spinner" loading />
									: null}

						)}

				{ maxPage > 1 ?
					<Menu pagination>
						{ page > 4 ?
							<Menu.Item onClick={e => this.changePage(e, 0)}>
								1
							</Menu.Item>
						: null }
						{ page > 5 ?
							<Menu.Item onClick={e => this.changePage(e, page - 5)}>
								...
							</Menu.Item>
						: null }
						{ _.map(_.range(Math.max(0, page - 4), Math.min(maxPage, page + 5)), p => (
							<Menu.Item key={p} onClick={e => this.changePage(e, p)} active={p === page}>
								{p + 1}
							</Menu.Item>
						))}
						{ page < maxPage - 6 ?
							<Menu.Item onClick={e => this.changePage(e, page + 5)}>
								...
							</Menu.Item>
						: null }
						{ page < maxPage - 5 ?
							<Menu.Item onClick={e => this.changePage(e, maxPage - 1)}>
								{maxPage}
							</Menu.Item>
						: null }
					</Menu>
				: null }

				{this.state.rules ?
					<Modal open={this.state.modal} onClose={this.toggleModal}>
						<Modal.Header>
							Game Rules for '{this.state.rulesWorld.name}'
							({this.state.rulesWorld.dimensionType.name})
						</Modal.Header>
						<Modal.Content>
							<Grid columns={2}>
								<Grid.Column>

Name

Value

											{_.map(_.slice(this.state.rules, 0, this.state.rules.length / 2), rule =>

{rule.name}

														{rule.value === "true" || rule.value === "false" ?
															<Radio
																toggle checked={rule.value === "true"}
																onClick={() => this.toggleRule(rule.name)}
															/>
														: rule.value}

											)}

								</Grid.Column>

								<Grid.Column>

Name

Value

											{_.map(_.slice(this.state.rules, this.state.rules.length / 2), rule =>

{rule.name}

														{rule.value === "true" || rule.value === "false" ?
															<Radio
																toggle checked={rule.value === "true"}
																onClick={() => this.toggleRule(rule.name)}
															/>
														: rule.value}

											)}

								</Grid.Column>
							</Grid>
						</Modal.Content>
						<Modal.Actions>
							<Button color="blue" onClick={this.saveGameRules}>Save</Button>
							<Button onClick={this.toggleModal}>Cancel</Button>
						</Modal.Actions>
					</Modal>
				: null}

			</Segment>
		)
	}
}

const mapStateToProps = (_state) => {
	const state = _state.world

	return {
		worlds: state.worlds,
		dimTypes: _state.api.types[DIM_TYPES],
		genTypes: _state.api.types[GEN_TYPES],
		diffTypes: _state.api.types[DIFF_TYPES],
		gmTypes: _state.api.types[GM_TYPES],
		creating: state.creating,
	}
}

const mapDispatchToProps = (dispatch) => {
	return {
		requestWorlds: () => dispatch(requestWorlds(true)),
		requestCatalog: type => dispatch(requestCatalog(type)),
		requestChangeWorld: (uuid, data, op) => dispatch(requestChangeWorld(uuid, data, op)),
		requestCreateWorld: data => dispatch(requestCreateWorld(data)),
		requestDeleteWorld: uuid => dispatch(requestDeleteWorld(uuid)),
	}
}

export default connect(mapStateToProps, mapDispatchToProps)(Worlds);
<|endoftext|>


<commit_before>
var util = require('util');
var http = require('http');
var path = require('path');
var events = require('events');

var common = require('./common.js');
var utils = common.load('utils');
var configs = common.load('config.json');

var configure = common.load('helpers', 'configure');
var handler = common.load('core', 'handler');
var requires = common.load('core', 'require');

function Piccolo() {

  // catches
  this.requireCache = {};
  this.preloaded = configs.natives.concat(['changeTable']);

  // settings objects
  this.configs = configure.configs;
  this.settings = {};

  // Emit ready event once all async startups has completed
  this.progress = new utils.ProgressTracker(this.emit.bind(this, 'ready'));
  this.progress.add(['structure', 'route']);

  // List all servers
  this.query = [];
  this.servers = [];

  // Will contain the main router
  this.router = null;

  // Preconfigure settings
  this.set('router', common.find('defaults', 'router'));
  this.set('root', path.dirname(module.parent.filename));
}
util.inherits(Piccolo, events.EventEmitter);

module.exports = function () { return new Piccolo(); };

Piccolo.prototype.configure = function (condition, method) {

  method = typeof condition === 'function' ? condition : method;
  condition = typeof condition === 'string' ? condition : 'default';

  // Save condition configureation until use is executed
  this.configs[condition] = method;
};

Piccolo.prototype.use = function (condition) {

  // Execute the default method
  this.configs['default'].call(this);

  // Execute conditional method
  var method = this.configs[condition];
  if (method) {
    method.call(this);
  } else {
    this.emit('error', new Error('The configure condition ' + condition + ' do not exist'));
  }

  // Remove configureations
  this.configs = {};
};

Piccolo.prototype.set = function (property, value) {
  // Do not allow set, when an server is applyed to the framework
  if (this.servers.length) {
    this.emit('error', new Error('can not set setting after an server is attached'));
    return;
  }

  if (configure.update[property] === undefined) {
    this.emit('error', new Error('the ' + property + ' setting do not exist'));
  }

  // Update settings property by executeing function
  return configure.update[property].call(this, this.settings, value);
};

Piccolo.prototype.get = function (property) {
  if (configure.update[property] === undefined) {
    this.emit('error', new Error('the ' + property + ' setting do not exist'));
  }

  // Get settings property
  return this.settings[property];
};

// Require async module
Piccolo.prototype.require = function (filename /* notStatic, callback */) {
  var callback = arguments[arguments.length - 1];

  // Check filename argument
  if (typeof filename !== 'string') {
    throw new Error('No filename was given');
  }

  // Check callback argument
  if (typeof callback !== 'function') {
    throw new Error('No callback was given');
  }

  // check if the request is the router module
  if (filename === 'router') {
    callback(null, this.router);
    return;
  }

  // If three arguments was given, then don't parse the filename relative to static
  if (arguments.length !== 3 && this.preloaded.indexOf(filename) === -1) {
    filename = path.join(this.get('static'), './' + filename);
  }

  // Use the documented staticRequire method
  requires.requireModule(this, filename, callback);
};

// Attach a handler to a server instance
Piccolo.prototype.listen = function (server) {
  var self = this;

  // Standart port number
  if (typeof server === 'number') {
    server = http.createServer().listen(arguments[0], arguments[1]);
  }

  // Attach the server
  if (this.router) {
    this.servers.push(server);
    server.on('request', self.handlers);
    return;
  }

  // First attach create route load handler
  if (this.routeLoader === undefined) {
    this.routeLoader = new utils.mutiplyLoader(function (error, router) {
      // Cleanup
      delete self.routeLoader;

      // Stop in case of an error
      if (error) {
        self.emit('error', error);
        return;
      }

      // Save router module
      self.router = router;

      // Setup handler
      self.handler = handler.setup(self);
    });

    // Require route module
    this.require(this.settings.router, true, function (error, router) {
      self.routeLoader.done(error, router);
    });
  }

  if (this.router === null) {
    this.routeLoader.push(function (error) {
      if (error) return;

      // Attach handler to server
      self.servers.push(server);
      server.on('request', self.handler);
    });
  }
};
<commit_msg>[refactor] decouple router - needs cleanup<commit_after>
var util = require('util');
var http = require('http');
var path = require('path');
var events = require('events');

var common = require('./common.js');
var utils = common.load('utils');
var configs = common.load('config.json');

var configure = common.load('helpers', 'configure');
var handler = common.load('core', 'handler');
var requires = common.load('core', 'require');

function Piccolo() {

  // catches
  this.requireCache = {};
  this.preloaded = configs.natives.concat(['changeTable']);

  // settings objects
  this.configs = configure.configs;
  this.settings = {};

  // Emit ready event once all async startups has completed
  this.progress = new utils.ProgressTracker(this.emit.bind(this, 'ready'));
  this.progress.add(['structure', 'route']);

  // List all servers
  this.query = [];
  this.servers = [];

  // Will contain the main router
  this.router = null;

  // Preconfigure settings
  this.set('router', common.find('defaults', 'router'));
  this.set('root', path.dirname(module.parent.filename));
}
util.inherits(Piccolo, events.EventEmitter);

module.exports = function () { return new Piccolo(); };

Piccolo.prototype.configure = function (condition, method) {

  method = typeof condition === 'function' ? condition : method;
  condition = typeof condition === 'string' ? condition : 'default';

  // Save condition configureation until use is executed
  this.configs[condition] = method;
};

Piccolo.prototype.use = function (condition) {

  // Execute the default method
  this.configs['default'].call(this);

  // Execute conditional method
  var method = this.configs[condition];
  if (method) {
    method.call(this);
  } else {
    this.emit('error', new Error('The configure condition ' + condition + ' do not exist'));
  }

  // Remove configureations
  this.configs = {};
};

Piccolo.prototype.set = function (property, value) {
  // Do not allow set, when an server is applyed to the framework
  if (this.servers.length) {
    this.emit('error', new Error('can not set setting after an server is attached'));
    return;
  }

  if (configure.update[property] === undefined) {
    this.emit('error', new Error('the ' + property + ' setting do not exist'));
  }

  // Update settings property by executeing function
  return configure.update[property].call(this, this.settings, value);
};

Piccolo.prototype.get = function (property) {
  if (configure.update[property] === undefined) {
    this.emit('error', new Error('the ' + property + ' setting do not exist'));
  }

  // Get settings property
  return this.settings[property];
};

// Require async module
Piccolo.prototype.require = function (filename /* notStatic, callback */) {
  var callback = arguments[arguments.length - 1];

  // Check filename argument
  if (typeof filename !== 'string') {
    throw new Error('No filename was given');
  }

  // Check callback argument
  if (typeof callback !== 'function') {
    throw new Error('No callback was given');
  }

  // check if the request is the router module
  if (filename === 'router') {
    filename = this.settings.router;
  }

  // If three arguments was given, then don't parse the filename relative to static
  else if (arguments.length !== 3 && this.preloaded.indexOf(filename) === -1) {
    filename = path.join(this.get('static'), './' + filename);
  }

  // Use the documented staticRequire method
  requires.requireModule(this, filename, callback);
};

// setup request constructor
Piccolo.prototype.setup = function () {
  var self = this;

  this.require('router', function (error, module) {
    if (error) return self.emit('error', error);

    self.router = module;
    self.Request = handler.setup(self);
  });
};

// will be overwriten
Piccolo.prototype.route = function (req, res) {
  if (this.Request === undefined) {
    var emitter = new events.EventEmitter();

    process.nextTick(function () {
      emitter.emit('error', new Error('piccolo object not ready'));
    });

    return emitter;
  }

  return new this.Request(req, res);
};
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');

class Singleton {
  constructor(options = {}) {
    assert(options.name, '[egg:singleton] Singleton#constructor options.name is required');
    assert(options.app, '[egg:singleton] Singleton#constructor options.app is required');
    assert(options.create, '[egg:singleton] Singleton#constructor options.create is required');
    assert(!options.app[options.name], `${options.name} is already exists in app`);
    this.clients = new Map();
    this.app = options.app;
    this.name = options.name;
    this.create = options.create;
    /* istanbul ignore next */
    this.options = options.app.config[this.name] || {};
  }

  init() {
    const options = this.options;
    assert(!(options.client && options.clients),
      `eggg:singleton ${this.name} can not set options.client and options.clients both`);

    // alias app[name] as client, but still support createInstance method
    if (options.client) {
      const client = this.createInstance(options.client);
      this.app[this.name] = client;
      assert(!client.createInstance, 'singleton instance should not have createInstance method');
      client.createInstance = this.createInstance.bind(this);
      return;
    }

    // multi clent, use app[name].getInstance(id)
    if (options.clients) {
      for (const id in options.clients) {
        this.clients.set(id, this.createInstance(options.clients[id]));
      }
      this.app[this.name] = this;
      return;
    }

    // no config.clients and config.client
    this.app[this.name] = this;
  }

  get(id) {
    return this.clients.get(id);
  }

  createInstance(config) {
    // options.default will be merge in to options.clients[id]
    config = Object.assign({}, this.options.default, config);
    return this.create(config, this.app);
  }
}

module.exports = Singleton;
<commit_msg>chore:  typo eggg=>egg (#881)<commit_after>'use strict';

const assert = require('assert');

class Singleton {
  constructor(options = {}) {
    assert(options.name, '[egg:singleton] Singleton#constructor options.name is required');
    assert(options.app, '[egg:singleton] Singleton#constructor options.app is required');
    assert(options.create, '[egg:singleton] Singleton#constructor options.create is required');
    assert(!options.app[options.name], `${options.name} is already exists in app`);
    this.clients = new Map();
    this.app = options.app;
    this.name = options.name;
    this.create = options.create;
    /* istanbul ignore next */
    this.options = options.app.config[this.name] || {};
  }

  init() {
    const options = this.options;
    assert(!(options.client && options.clients),
      `egg:singleton ${this.name} can not set options.client and options.clients both`);

    // alias app[name] as client, but still support createInstance method
    if (options.client) {
      const client = this.createInstance(options.client);
      this.app[this.name] = client;
      assert(!client.createInstance, 'singleton instance should not have createInstance method');
      client.createInstance = this.createInstance.bind(this);
      return;
    }

    // multi clent, use app[name].getInstance(id)
    if (options.clients) {
      for (const id in options.clients) {
        this.clients.set(id, this.createInstance(options.clients[id]));
      }
      this.app[this.name] = this;
      return;
    }

    // no config.clients and config.client
    this.app[this.name] = this;
  }

  get(id) {
    return this.clients.get(id);
  }

  createInstance(config) {
    // options.default will be merge in to options.clients[id]
    config = Object.assign({}, this.options.default, config);
    return this.create(config, this.app);
  }
}

module.exports = Singleton;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
  View,
  TouchableOpacity,
  LayoutAnimation,
  Platform,
  UIManager,
  StatusBar,
  StyleSheet,
  Image
} from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import { getColor } from '../components/config'
import Background from '../components/background'
import InitialView from '../components/login_screen/initial_view'
import SignInForm from '../components/login_screen/signIn_form'
import SignUpForm from '../components/login_screen/signUp_form'
import ForgotPassForm from '../components/login_screen/forgotPassword_form'
import { firebaseApp } from '../firebase'
import { observer, inject } from 'mobx-react/native'
import { Actions } from 'react-native-mobx'

@inject("appStore") @observer
export default class LoginScreen extends Component {
  constructor(props) {
    super(props)
    this.state = {
      initialScreen: false,
      signIn: false,
      signUp: false,
      forgotPass: false
    }
    if (Platform.OS === 'android') {
      UIManager.setLayoutAnimationEnabledExperimental(true)
    }
    _unsubscribe = firebaseApp.auth().onAuthStateChanged((user) => {
      if (user) {
        console.log(" --- User Signed In ---> " + user.displayName)
        this.props.appStore.user = user
        this.props.appStore.username = user.displayName
        Actions.home({ type: 'replace' })
      }
      else {
        console.log(" --- User is Signed Off --- ")
        this.setState({ initialScreen: true })
      }
      _unsubscribe()
    })
  }

  componentWillUnmount() {
  }

  componentWillMount() {
  }

  componentDidMount() {
  }

  componentDidUpdate() {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.spring)
  }

  render() {
    const initialView = this.state.initialScreen ?
      <InitialView
      onSignIn={this._onSignIn}
      onSignUp={this._onSignUp}
      animDelay={0}/>
    : null

    const signIn = this.state.signIn ?
      <SignInForm
      onBackFromSignIn={this._onBackFromSignIn}
      onForgotPass = {this._onForgotPass} />
    : null

    const signUp = this.state.signUp ?
      <SignUpForm
      onBackFromSignUp={this._onBackFromSignUp} />
    : null

    const fogotPass = this.state.forgotPass ?
      <ForgotPassForm
      onBackFromForgotPass={this._onBackFromForgotPass} />
    : null

    return (
      <View style={styles.container}>
        <KeyboardAwareScrollView contentContainerStyle={styles.scrollContainer} >
        <Background imgSrouce={require('../assets/images/bk.png')}/>
          <StatusBar
            backgroundColor={getColor('googleBlue700')}
            barStyle='light-content'
            animated={true}
          />
          <View style={styles.logoContainer}>
            <TouchableOpacity onPress={this._onLogoClicked}>
              <Image source={require('../assets/images/jsapp.png')} style={styles.logoImage}/>
            </TouchableOpacity>
          </View>
          { initialView }
          { signIn }
          { signUp }
          { fogotPass }
        </KeyboardAwareScrollView>
      </View>
    )
  }

  _onLogoClicked = () => {
    this.setState({
      initialScreen: true,
      signIn: false,
      signUp: false,
      forgotPass: false
    })
  }

  _onSignIn = () => {
    this.setState({
      initialScreen: false,
      signIn: true
    })
  }

  _onBackFromSignIn = () => {
    this.setState({
      initialScreen: true,
      signIn: false
    })
  }

  _onSignUp = () => {
    this.setState({
      initialScreen: false,
      signUp: true
    })
  }

  _onBackFromSignUp = () => {
    this.setState({
      initialScreen: true,
      signUp: false
    })
  }

  _onForgotPass = () => {
    this.setState({
      initialScreen: false,
      signIn: false,
      signUp: false,
      forgotPass: true
    })
  }

  _onBackFromForgotPass = () => {
    this.setState({
      initialScreen: true,
      forgotPass: false
    })
  }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  scrollContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  logoContainer: {
  },
  logoImage : {
    height: 300,
    width: 300,
  },
})
<commit_msg>polishing the login screen<commit_after>import React, { Component } from 'react'
import {
  View,
  TouchableOpacity,
  LayoutAnimation,
  Platform,
  UIManager,
  StatusBar,
  StyleSheet,
  Image
} from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import { getColor } from '../components/config'
import Background from '../components/background'
import InitialView from '../components/login_screen/initial_view'
import SignInForm from '../components/login_screen/signIn_form'
import SignUpForm from '../components/login_screen/signUp_form'
import ForgotPassForm from '../components/login_screen/forgotPassword_form'
import { firebaseApp } from '../firebase'
import { observer, inject } from 'mobx-react/native'
import { Actions } from 'react-native-mobx'

@inject("appStore") @observer
export default class LoginScreen extends Component {
  constructor(props) {
    super(props)
    this.state = {
      initialScreen: false,
      signIn: false,
      signUp: false,
      forgotPass: false
    }
    if (Platform.OS === 'android') {
      UIManager.setLayoutAnimationEnabledExperimental(true)
    }
    _unsubscribe = firebaseApp.auth().onAuthStateChanged((user) => {
      if (user) {
        console.log(" --- User Signed In ---> " + user.displayName)
        this.props.appStore.user = user
        this.props.appStore.username = user.displayName
        Actions.home({ type: 'replace' })
      }
      else {
        console.log(" --- User is Signed Off --- ")
        this.setState({ initialScreen: true })
      }
      _unsubscribe()
    })
  }

  componentWillUnmount() {
  }

  componentWillMount() {
  }

  componentDidMount() {
  }

  componentDidUpdate() {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.spring)
  }

  render() {
    const initialView = this.state.initialScreen ?
      <InitialView
      onSignIn={this._onSignIn}
      onSignUp={this._onSignUp}
      animDelay={0}/>
    : null

    const signIn = this.state.signIn ?
      <SignInForm
      onBackFromSignIn={this._onBackFromSignIn}
      onForgotPass = {this._onForgotPass} />
    : null

    const signUp = this.state.signUp ?
      <SignUpForm
      onBackFromSignUp={this._onBackFromSignUp} />
    : null

    const fogotPass = this.state.forgotPass ?
      <ForgotPassForm
      onBackFromForgotPass={this._onBackFromForgotPass} />
    : null

    return (
      <View style={styles.container}>
      <Background imgSrouce={require('../assets/images/bk.png')}/>
      <KeyboardAwareScrollView contentContainerStyle={styles.scrollContainer} >
        <StatusBar
          backgroundColor={getColor('googleBlue700')}
          barStyle='light-content'
          animated={true}
        />
        <View style={styles.logoContainer}>
          <TouchableOpacity onPress={this._onLogoClicked}>
            <Image source={require('../assets/images/jsapp.png')} style={styles.logoImage}/>
          </TouchableOpacity>
        </View>
        { initialView }
        { signIn }
        { signUp }
        { fogotPass }
      </KeyboardAwareScrollView>
      </View>
    )
  }

  _onLogoClicked = () => {
    this.setState({
      initialScreen: true,
      signIn: false,
      signUp: false,
      forgotPass: false
    })
  }

  _onSignIn = () => {
    this.setState({
      initialScreen: false,
      signIn: true
    })
  }

  _onBackFromSignIn = () => {
    this.setState({
      initialScreen: true,
      signIn: false
    })
  }

  _onSignUp = () => {
    this.setState({
      initialScreen: false,
      signUp: true
    })
  }

  _onBackFromSignUp = () => {
    this.setState({
      initialScreen: true,
      signUp: false
    })
  }

  _onForgotPass = () => {
    this.setState({
      initialScreen: false,
      signIn: false,
      signUp: false,
      forgotPass: true
    })
  }

  _onBackFromForgotPass = () => {
    this.setState({
      initialScreen: true,
      forgotPass: false
    })
  }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  scrollContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  logoContainer: {
  },
  logoImage : {
    height: 300,
    width: 300,
  },
})
<|endoftext|>


<commit_before>/*
  SystemJS Loader Plugin Support

  Supports plugin loader syntax with "!", or via metadata.loader

  The plugin name is loaded as a module itself, and can override standard loader hooks
  for the plugin resource. See the plugin section of the systemjs readme.
*/

(function() {
  function getParentName(loader, parentName) {
    // if parent is a plugin, normalize against the parent plugin argument only
    if (parentName) {
      var parentPluginIndex;
      if (loader.pluginFirst) {
        if ((parentPluginIndex = parentName.lastIndexOf('!')) != -1)
          return parentName.substr(parentPluginIndex + 1);
      }
      else {
        if ((parentPluginIndex = parentName.indexOf('!')) != -1)
          return parentName.substr(0, parentPluginIndex);
      }

      return parentName;
    }
  }

  function parsePlugin(loader, name) {
    var argumentName;
    var pluginName;

    var pluginIndex = name.lastIndexOf('!');

    if (pluginIndex == -1)
      return;

    if (loader.pluginFirst) {
      argumentName = name.substr(pluginIndex + 1);
      pluginName = name.substr(0, pluginIndex);
    }
    else {
      argumentName = name.substr(0, pluginIndex);
      pluginName = name.substr(pluginIndex + 1) || argumentName.substr(argumentName.lastIndexOf('.') + 1);
    }

    return {
      argument: argumentName,
      plugin: pluginName
    };
  }

  // put name back together after parts have been normalized
  function combinePluginParts(loader, argumentName, pluginName, defaultExtension) {
    if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js')
      argumentName = argumentName.substr(0, argumentName.length - 3);

    if (loader.pluginFirst) {
      return pluginName + '!' + argumentName;
    }
    else {
      return argumentName + '!' + pluginName;
    }
  }

  // note if normalize will add a default js extension
  // if so, remove for backwards compat
  // this is strange and sucks, but will be deprecated
  function checkDefaultExtension(loader, arg) {
    return loader.defaultJSExtensions && arg.substr(arg.length - 3, 3) != '.js';
  }

  function createNormalizeSync(normalizeSync) {
    return function(name, parentName, isPlugin) {
      var loader = this;

      parentName = getParentName(this, parentName);
      var parsed = parsePlugin(loader, name);

      if (!parsed)
        return normalizeSync.call(this, name, parentName, isPlugin);

      // if this is a plugin, normalize the plugin name and the argument
      var argumentName = loader.normalizeSync(parsed.argument, parentName, true);
      var pluginName = loader.normalizeSync(parsed.plugin, parentName, true);
      return combinePluginParts(loader, argumentName, pluginName, checkDefaultExtension(loader, parsed.argument));
    };
  }

  hook('decanonicalize', createNormalizeSync);
  hook('normalizeSync', createNormalizeSync);

  hook('normalize', function(normalize) {
    return function(name, parentName, isPlugin) {
      var loader = this;

      parentName = getParentName(this, parentName);

      var parsed = parsePlugin(loader, name);

      if (!parsed)
        return normalize.call(loader, name, parentName, isPlugin);

      return Promise.all([
        loader.normalize(parsed.argument, parentName, true),
        loader.normalize(parsed.plugin, parentName)
      ])
      .then(function(normalized) {
        return combinePluginParts(loader, normalized[0], normalized[1], checkDefaultExtension(loader, parsed.argument));
      });
    }
  });

  hook('locate', function(locate) {
    return function(load) {
      var loader = this;

      var name = load.name;

      // plugin syntax
      var pluginSyntaxIndex;
      if (loader.pluginFirst) {
        if ((pluginSyntaxIndex = name.indexOf('!')) != -1) {
          load.metadata.loader = name.substr(0, pluginSyntaxIndex);
          load.name = name.substr(pluginSyntaxIndex + 1);
        }
      }
      else {
        if ((pluginSyntaxIndex = name.lastIndexOf('!')) != -1) {
          load.metadata.loader = name.substr(pluginSyntaxIndex + 1);
          load.name = name.substr(0, pluginSyntaxIndex);
        }
      }

      return locate.call(loader, load)
      .then(function(address) {
        if (pluginSyntaxIndex != -1 || !load.metadata.loader)
          return address;

        // normalize plugin relative to parent in locate here when
        // using plugin via loader metadata
        return loader.normalize(load.metadata.loader, load.name)
        .then(function(loaderNormalized) {
          load.metadata.loader = loaderNormalized;
          return address;
        });
      })
      .then(function(address) {
        var plugin = load.metadata.loader;

        if (!plugin)
          return address;

        // only fetch the plugin itself if this name isn't defined
        if (loader.defined && loader.defined[name])
          return address;

        var pluginLoader = loader.pluginLoader || loader;

        // load the plugin module and run standard locate
        return pluginLoader['import'](plugin)
        .then(function(loaderModule) {
          // store the plugin module itself on the metadata
          load.metadata.loaderModule = loaderModule;

          load.address = address;
          if (loaderModule.locate)
            return loaderModule.locate.call(loader, load);

          return address;
        });
      });
    };
  });

  hook('fetch', function(fetch) {
    return function(load) {
      var loader = this;
      if (load.metadata.loaderModule && load.metadata.loaderModule.fetch && load.metadata.format != 'defined') {
        load.metadata.scriptLoad = false;
        return load.metadata.loaderModule.fetch.call(loader, load, function(load) {
          return fetch.call(loader, load);
        });
      }
      else {
        return fetch.call(loader, load);
      }
    };
  });

  hook('translate', function(translate) {
    return function(load) {
      var loader = this;
      if (load.metadata.loaderModule && load.metadata.loaderModule.translate && load.metadata.format != 'defined') {
        return Promise.resolve(load.metadata.loaderModule.translate.call(loader, load)).then(function(result) {
          var sourceMap = load.metadata.sourceMap;

          // sanitize sourceMap if an object not a JSON string
          if (sourceMap) {
            if (typeof sourceMap != 'object')
              throw new Error('load.metadata.sourceMap must be set to an object.');

            var originalName = load.name.split('!')[0];

            // force set the filename of the original file
            sourceMap.file = originalName + '!transpiled';

            // force set the sources list if only one source
            if (!sourceMap.sources || sourceMap.sources.length <= 1)
              sourceMap.sources = [originalName];
          }

          // if running on file:/// URLs, sourcesContent is necessary
          // load.metadata.sourceMap.sourcesContent = [load.source];

          if (typeof result == 'string')
            load.source = result;
          else
            warn.call(this, 'Plugin ' + load.metadata.loader + ' should return the source in translate, instead of setting load.source directly. This support will be deprecated.');

          return translate.call(loader, load);
        });
      }
      else {
        return translate.call(loader, load);
      }
    };
  });

  hook('instantiate', function(instantiate) {
    return function(load) {
      var loader = this;

      if (load.metadata.loaderModule && load.metadata.loaderModule.instantiate && !loader.builder && load.metadata.format != 'defined')
        return Promise.resolve(load.metadata.loaderModule.instantiate.call(loader, load)).then(function(result) {
          load.metadata.entry = createEntry();
          load.metadata.entry.execute = function() {
            return result;
          }
          load.metadata.entry.deps = load.metadata.deps;
          load.metadata.format = 'defined';
          return instantiate.call(loader, load);
        });
      else
        return instantiate.call(loader, load);
    };
  });

})();<commit_msg>plugin instantiate hook flexibility<commit_after>/*
  SystemJS Loader Plugin Support

  Supports plugin loader syntax with "!", or via metadata.loader

  The plugin name is loaded as a module itself, and can override standard loader hooks
  for the plugin resource. See the plugin section of the systemjs readme.
*/

(function() {
  function getParentName(loader, parentName) {
    // if parent is a plugin, normalize against the parent plugin argument only
    if (parentName) {
      var parentPluginIndex;
      if (loader.pluginFirst) {
        if ((parentPluginIndex = parentName.lastIndexOf('!')) != -1)
          return parentName.substr(parentPluginIndex + 1);
      }
      else {
        if ((parentPluginIndex = parentName.indexOf('!')) != -1)
          return parentName.substr(0, parentPluginIndex);
      }

      return parentName;
    }
  }

  function parsePlugin(loader, name) {
    var argumentName;
    var pluginName;

    var pluginIndex = name.lastIndexOf('!');

    if (pluginIndex == -1)
      return;

    if (loader.pluginFirst) {
      argumentName = name.substr(pluginIndex + 1);
      pluginName = name.substr(0, pluginIndex);
    }
    else {
      argumentName = name.substr(0, pluginIndex);
      pluginName = name.substr(pluginIndex + 1) || argumentName.substr(argumentName.lastIndexOf('.') + 1);
    }

    return {
      argument: argumentName,
      plugin: pluginName
    };
  }

  // put name back together after parts have been normalized
  function combinePluginParts(loader, argumentName, pluginName, defaultExtension) {
    if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js')
      argumentName = argumentName.substr(0, argumentName.length - 3);

    if (loader.pluginFirst) {
      return pluginName + '!' + argumentName;
    }
    else {
      return argumentName + '!' + pluginName;
    }
  }

  // note if normalize will add a default js extension
  // if so, remove for backwards compat
  // this is strange and sucks, but will be deprecated
  function checkDefaultExtension(loader, arg) {
    return loader.defaultJSExtensions && arg.substr(arg.length - 3, 3) != '.js';
  }

  function createNormalizeSync(normalizeSync) {
    return function(name, parentName, isPlugin) {
      var loader = this;

      parentName = getParentName(this, parentName);
      var parsed = parsePlugin(loader, name);

      if (!parsed)
        return normalizeSync.call(this, name, parentName, isPlugin);

      // if this is a plugin, normalize the plugin name and the argument
      var argumentName = loader.normalizeSync(parsed.argument, parentName, true);
      var pluginName = loader.normalizeSync(parsed.plugin, parentName, true);
      return combinePluginParts(loader, argumentName, pluginName, checkDefaultExtension(loader, parsed.argument));
    };
  }

  hook('decanonicalize', createNormalizeSync);
  hook('normalizeSync', createNormalizeSync);

  hook('normalize', function(normalize) {
    return function(name, parentName, isPlugin) {
      var loader = this;

      parentName = getParentName(this, parentName);

      var parsed = parsePlugin(loader, name);

      if (!parsed)
        return normalize.call(loader, name, parentName, isPlugin);

      return Promise.all([
        loader.normalize(parsed.argument, parentName, true),
        loader.normalize(parsed.plugin, parentName)
      ])
      .then(function(normalized) {
        return combinePluginParts(loader, normalized[0], normalized[1], checkDefaultExtension(loader, parsed.argument));
      });
    }
  });

  hook('locate', function(locate) {
    return function(load) {
      var loader = this;

      var name = load.name;

      // plugin syntax
      var pluginSyntaxIndex;
      if (loader.pluginFirst) {
        if ((pluginSyntaxIndex = name.indexOf('!')) != -1) {
          load.metadata.loader = name.substr(0, pluginSyntaxIndex);
          load.name = name.substr(pluginSyntaxIndex + 1);
        }
      }
      else {
        if ((pluginSyntaxIndex = name.lastIndexOf('!')) != -1) {
          load.metadata.loader = name.substr(pluginSyntaxIndex + 1);
          load.name = name.substr(0, pluginSyntaxIndex);
        }
      }

      return locate.call(loader, load)
      .then(function(address) {
        if (pluginSyntaxIndex != -1 || !load.metadata.loader)
          return address;

        // normalize plugin relative to parent in locate here when
        // using plugin via loader metadata
        return loader.normalize(load.metadata.loader, load.name)
        .then(function(loaderNormalized) {
          load.metadata.loader = loaderNormalized;
          return address;
        });
      })
      .then(function(address) {
        var plugin = load.metadata.loader;

        if (!plugin)
          return address;

        // only fetch the plugin itself if this name isn't defined
        if (loader.defined && loader.defined[name])
          return address;

        var pluginLoader = loader.pluginLoader || loader;

        // load the plugin module and run standard locate
        return pluginLoader['import'](plugin)
        .then(function(loaderModule) {
          // store the plugin module itself on the metadata
          load.metadata.loaderModule = loaderModule;

          load.address = address;
          if (loaderModule.locate)
            return loaderModule.locate.call(loader, load);

          return address;
        });
      });
    };
  });

  hook('fetch', function(fetch) {
    return function(load) {
      var loader = this;
      if (load.metadata.loaderModule && load.metadata.loaderModule.fetch && load.metadata.format != 'defined') {
        load.metadata.scriptLoad = false;
        return load.metadata.loaderModule.fetch.call(loader, load, function(load) {
          return fetch.call(loader, load);
        });
      }
      else {
        return fetch.call(loader, load);
      }
    };
  });

  hook('translate', function(translate) {
    return function(load) {
      var loader = this;
      if (load.metadata.loaderModule && load.metadata.loaderModule.translate && load.metadata.format != 'defined') {
        return Promise.resolve(load.metadata.loaderModule.translate.call(loader, load)).then(function(result) {
          var sourceMap = load.metadata.sourceMap;

          // sanitize sourceMap if an object not a JSON string
          if (sourceMap) {
            if (typeof sourceMap != 'object')
              throw new Error('load.metadata.sourceMap must be set to an object.');

            var originalName = load.name.split('!')[0];

            // force set the filename of the original file
            sourceMap.file = originalName + '!transpiled';

            // force set the sources list if only one source
            if (!sourceMap.sources || sourceMap.sources.length <= 1)
              sourceMap.sources = [originalName];
          }

          // if running on file:/// URLs, sourcesContent is necessary
          // load.metadata.sourceMap.sourcesContent = [load.source];

          if (typeof result == 'string')
            load.source = result;
          else
            warn.call(this, 'Plugin ' + load.metadata.loader + ' should return the source in translate, instead of setting load.source directly. This support will be deprecated.');

          return translate.call(loader, load);
        });
      }
      else {
        return translate.call(loader, load);
      }
    };
  });

  hook('instantiate', function(instantiate) {
    return function(load) {
      var loader = this;
      var calledInstantiate = false;

      if (load.metadata.loaderModule && load.metadata.loaderModule.instantiate && !loader.builder && load.metadata.format != 'defined')
        return Promise.resolve(load.metadata.loaderModule.instantiate.call(loader, load, function(load) {
          if (calledInstantiate)
            throw new Error('Instantiate must only be called once.');
          calledInstantiate = true;
          return instantiate.call(loader, load);
        })).then(function(result) {
          if (calledInstantiate)
            return result;

          load.metadata.entry = createEntry();
          load.metadata.entry.execute = function() {
            return result;
          }
          load.metadata.entry.deps = load.metadata.deps;
          load.metadata.format = 'defined';
          return instantiate.call(loader, load);
        });
      else
        return instantiate.call(loader, load);
    };
  });

})();<|endoftext|>


<commit_before>var _ = require('underscore');
var log = require('cdb.log');
var TileLayer = require('../geo/map/tile-layer');
var WMSLayer = require('../geo/map/wms-layer');
var GMapsBaseLayer = require('../geo/map/gmaps-base-layer');
var PlainLayer = require('../geo/map/plain-layer');
var CartoDBLayer = require('../geo/map/cartodb-layer');
var TorqueLayer = require('../geo/map/torque-layer');
var RasterLayer = require('../geo/map/raster-layer');

/*
 *  if we are using http and the tiles of base map need to be fetched from
 *  https try to fix it
 */
var HTTPS_TO_HTTP = {
  'https://dnv9my2eseobd.cloudfront.net/': 'http://a.tiles.mapbox.com/',
  'https://maps.nlp.nokia.com/': 'http://maps.nlp.nokia.com/',
  'https://tile.stamen.com/': 'http://tile.stamen.com/',
  'https://{s}.maps.nlp.nokia.com/': 'http://{s}.maps.nlp.nokia.com/',
  'https://cartocdn_{s}.global.ssl.fastly.net/': 'http://{s}.api.cartocdn.com/',
  'https://cartodb-basemaps-{s}.global.ssl.fastly.net/': 'http://{s}.basemaps.cartocdn.com/'
};

function transformToHTTP (tilesTemplate) {
  for (var url in HTTPS_TO_HTTP) {
    if (tilesTemplate.indexOf(url) !== -1) {
      return tilesTemplate.replace(url, HTTPS_TO_HTTP[url]);
    }
  }
  return tilesTemplate;
}

function transformToHTTPS (tilesTemplate) {
  for (var url in HTTPS_TO_HTTP) {
    var httpsUrl = HTTPS_TO_HTTP[url];
    if (tilesTemplate.indexOf(httpsUrl) !== -1) {
      return tilesTemplate.replace(httpsUrl, url);
    }
  }
  return tilesTemplate;
}

var LAYER_CONSTRUCTORS = {
  tiled: function (data, options) {
    var visModel = options.vis;

    if (visModel.get('https') === true) {
      data.urlTemplate = transformToHTTPS(data.urlTemplate);
    } else if (visModel.get('https') === false) { // Checking for an explicit false value. If it's undefined the url is left as is.
      data.urlTemplate = transformToHTTP(data.urlTemplate);
    }

    return new TileLayer(data, {
      vis: options.vis
    });
  },

  wms: function (data, options) {
    return new WMSLayer(data);
  },

  gmapsbase: function (data, options) {
    return new GMapsBaseLayer(data);
  },

  plain: function (data, options) {
    return new PlainLayer(data, {
      vis: options.vis
    });
  },

  background: function (data, options) {
    return new PlainLayer(data, {
      vis: options.vis
    });
  },

  cartodb: function (data, options) {
    return new CartoDBLayer(data, {
      vis: options.vis
    });
  },

  raster_tileo: function(data, options) {
    return new RasterLayer(data, {
      vis: options.vis
    });
  },

  torque: function (attrs, options) {
    var windshaftSettings = options.windshaftSettings;

    attrs = _.extend(attrs, {
      user_name: windshaftSettings.userName,
      maps_api_template: windshaftSettings.urlTemplate,
      stat_tag: windshaftSettings.statTag,
      api_key: windshaftSettings.apiKey,
      auth_token: windshaftSettings.authToken
    });

    if (windshaftSettings.templateName) {
      attrs = _.extend(attrs, {
        named_map: {
          name: windshaftSettings.templateName
        }
      });
    }

    return new TorqueLayer(attrs, {
      vis: options.vis
    });
  }
};

var LayersFactory = function (deps) {
  if (!deps.visModel) throw new Error('visModel is required');
  if (!deps.windshaftSettings) throw new Error('windshaftSettings is required');

  this._visModel = deps.visModel;
  this._windshaftSettings = deps.windshaftSettings;
};

LayersFactory.prototype.createLayer = function (type, attrs) {
  var isRaster = function(attrs) {
    for (var lay in layersData) {
      if (!layersData[lay].options.source) continue;
      var src = !!attrs.source? attrs.source : attrs.options.source
      if (layersData[lay].options.source == src)
        return !!(layersData[lay].options.table_name.indexOf('_raster') != -1);
    }
  }
  if (type.toLowerCase() == 'cartodb' && isRaster(attrs)) {
      type = 'raster_tileo';
  }
  var LayerConstructor = LAYER_CONSTRUCTORS[type.toLowerCase()];
  if (!LayerConstructor) {
    log.error("error creating layer of type '" + type + "'");
    return null;
  }
  // Flatten "options"
  var layerAttributes = _.extend({}, _.omit(attrs, 'options'), attrs.options);

  return LayerConstructor(layerAttributes, {
    windshaftSettings: this._windshaftSettings,
    vis: this._visModel
  });
};

module.exports = LayersFactory;
<commit_msg>dont check tiled/plain layers<commit_after>var _ = require('underscore');
var log = require('cdb.log');
var TileLayer = require('../geo/map/tile-layer');
var WMSLayer = require('../geo/map/wms-layer');
var GMapsBaseLayer = require('../geo/map/gmaps-base-layer');
var PlainLayer = require('../geo/map/plain-layer');
var CartoDBLayer = require('../geo/map/cartodb-layer');
var TorqueLayer = require('../geo/map/torque-layer');
var RasterLayer = require('../geo/map/raster-layer');

/*
 *  if we are using http and the tiles of base map need to be fetched from
 *  https try to fix it
 */
var HTTPS_TO_HTTP = {
  'https://dnv9my2eseobd.cloudfront.net/': 'http://a.tiles.mapbox.com/',
  'https://maps.nlp.nokia.com/': 'http://maps.nlp.nokia.com/',
  'https://tile.stamen.com/': 'http://tile.stamen.com/',
  'https://{s}.maps.nlp.nokia.com/': 'http://{s}.maps.nlp.nokia.com/',
  'https://cartocdn_{s}.global.ssl.fastly.net/': 'http://{s}.api.cartocdn.com/',
  'https://cartodb-basemaps-{s}.global.ssl.fastly.net/': 'http://{s}.basemaps.cartocdn.com/'
};

function transformToHTTP (tilesTemplate) {
  for (var url in HTTPS_TO_HTTP) {
    if (tilesTemplate.indexOf(url) !== -1) {
      return tilesTemplate.replace(url, HTTPS_TO_HTTP[url]);
    }
  }
  return tilesTemplate;
}

function transformToHTTPS (tilesTemplate) {
  for (var url in HTTPS_TO_HTTP) {
    var httpsUrl = HTTPS_TO_HTTP[url];
    if (tilesTemplate.indexOf(httpsUrl) !== -1) {
      return tilesTemplate.replace(httpsUrl, url);
    }
  }
  return tilesTemplate;
}

var LAYER_CONSTRUCTORS = {
  tiled: function (data, options) {
    var visModel = options.vis;

    if (visModel.get('https') === true) {
      data.urlTemplate = transformToHTTPS(data.urlTemplate);
    } else if (visModel.get('https') === false) { // Checking for an explicit false value. If it's undefined the url is left as is.
      data.urlTemplate = transformToHTTP(data.urlTemplate);
    }

    return new TileLayer(data, {
      vis: options.vis
    });
  },

  wms: function (data, options) {
    return new WMSLayer(data);
  },

  gmapsbase: function (data, options) {
    return new GMapsBaseLayer(data);
  },

  plain: function (data, options) {
    return new PlainLayer(data, {
      vis: options.vis
    });
  },

  background: function (data, options) {
    return new PlainLayer(data, {
      vis: options.vis
    });
  },

  cartodb: function (data, options) {
    return new CartoDBLayer(data, {
      vis: options.vis
    });
  },

  raster_tileo: function(data, options) {
    return new RasterLayer(data, {
      vis: options.vis
    });
  },

  torque: function (attrs, options) {
    var windshaftSettings = options.windshaftSettings;

    attrs = _.extend(attrs, {
      user_name: windshaftSettings.userName,
      maps_api_template: windshaftSettings.urlTemplate,
      stat_tag: windshaftSettings.statTag,
      api_key: windshaftSettings.apiKey,
      auth_token: windshaftSettings.authToken
    });

    if (windshaftSettings.templateName) {
      attrs = _.extend(attrs, {
        named_map: {
          name: windshaftSettings.templateName
        }
      });
    }

    return new TorqueLayer(attrs, {
      vis: options.vis
    });
  }
};

var LayersFactory = function (deps) {
  if (!deps.visModel) throw new Error('visModel is required');
  if (!deps.windshaftSettings) throw new Error('windshaftSettings is required');

  this._visModel = deps.visModel;
  this._windshaftSettings = deps.windshaftSettings;
};

LayersFactory.prototype.createLayer = function (type, attrs) {
  var isRaster = function(attrs) {
    for (var lay in layersData) {
      if (!layersData[lay].options.source) continue;
      var src = !!attrs.source? attrs.source : attrs.options.source
      if (layersData[lay].options.source == src)
        return !!(layersData[lay].options.table_name.indexOf('_raster') != -1);
    }
  }
  if (attrs.type.toLowerCase() == 'cartodb' && isRaster(attrs)) {
      type = 'raster_tileo';
  }
  var LayerConstructor = LAYER_CONSTRUCTORS[type.toLowerCase()];
  if (!LayerConstructor) {
    log.error("error creating layer of type '" + type + "'");
    return null;
  }
  // Flatten "options"
  var layerAttributes = _.extend({}, _.omit(attrs, 'options'), attrs.options);

  return LayerConstructor(layerAttributes, {
    windshaftSettings: this._windshaftSettings,
    vis: this._visModel
  });
};

module.exports = LayersFactory;
<|endoftext|>


<commit_before>exports.categories = {
    'Authentication': {
        bell: {
            url: 'https://github.com/hapijs/bell',
            description: 'Third-party login plugin for hapi with built in Facebook, GitHub, Google, Twitter, Yahoo, Foursquare, and Windows Live support'
        },
        'hapi-auth-basic': {
            url: 'https://github.com/hapijs/hapi-auth-basic',
            description: 'An HTTP Basic authentication scheme'
        },
        'hapi-auth-bearer-token': {
            url: 'https://github.com/johnbrett/hapi-auth-bearer-token',
            description: 'A bearer token based authentication scheme'
        },
        'hapi-auth-cookie': {
            url: 'https://github.com/hapijs/hapi-auth-cookie',
            description: 'A cookie-based session authentication scheme'
        },
        'hapi-auth-hawk': {
            url :'https://github.com/hapijs/hapi-auth-hawk',
            description: 'Hawk authentication plugin'
        }
    },
    'Documentation': {
        lout: {
            url: 'https://github.com/hapijs/lout',
            description: 'A browsable documentation generator'
        }
    },
    'Logging/Metrics': {
        good: {
            url: 'https://github.com/hapijs/good',
            description: 'A logging plugin that supports output to console, file and udp/http endpoints'
        },
        'hapi-statsd': {
            url: 'https://github.com/mac-/hapi-statsd',
            description: 'Sends request round trip metrics to statsd'
        }
    },
    'Session': {
        crumb: {
            url: 'https://github.com/hapijs/crumb',
            description: 'CSRF crumb generation and validation for hapi'
        },
        yar: {
            url: 'https://github.com/hapijs/yar',
            description: 'A hapi session plugin and cookie jar'
        }
    },
    'Utility': {
        bassmaster: {
            url: 'https://github.com/hapijs/bassmaster',
            description: 'The batch endpoint makes it easy to combine multiple requests into a single one'
        },
        halacious: {
            url: 'https://github.com/bleupen/halacious',
            description: 'A HAL processor for hapi servers'
        },
        'hapi-mongoose-db-connector' : {
            url: 'https://github.com/codedoctor/hapi-mongoose-db-connector',
            description: 'hapi plugin that connects to mongodb for mongoose apps'
        },
        'hapi-route-directory': {
            url: 'https://github.com/clarkie/hapi-route-directory',
            description: 'A lightweight json route directory'
        },
        'hapi-routes-status' : {
          url: 'https://github.com/codedoctor/hapi-routes-status',
          description: 'Exposes a status route for your node.js/hapi projects.'
        },
        poop: {
            url: 'https://github.com/hapijs/poop',
            description: 'Plugin for taking a process dump and cleaning up after an uncaught exception'
        },
        reptile: {
            url: 'https://github.com/hapijs/reptile',
            description: 'A plugin for creating a REPL'
        },
        scooter: {
            url: 'https://github.com/hapijs/scooter',
            description: 'User-agent information plugin'
        },
        tv: {
            url: 'https://github.com/hapijs/tv',
            description: 'Interactive debug console plugin for hapi servers'
        }
    },
    'The extended hapi universe': {
        boom: {
            url: 'https://github.com/hapijs/boom',
            description: 'HTTP-friendly error objects'
        },
        confidence: {
            url: 'https://github.com/hapijs/confidence',
            description: 'A configuration document format, an API, and a foundation for A/B testing'
        },
        faketoe: {
            url: 'https://github.com/hapijs/faketoe',
            description: 'An XML to JSON converter'
        },
        hoek: {
            url: 'https://github.com/hapijs/hoek',
            description: 'General purpose node utilities'
        },
        joi: {
            url: 'https://github.com/hapijs/joi',
            description: 'Object schema description language and validator for JavaScript objects'
        },
        kilt: {
            url: 'https://github.com/hapijs/kilt',
            description: 'Combine multiple event emitters into a single emitter'
        },
        lab: {
            url: 'https://github.com/hapijs/lab',
            description: 'A simple testing utility with code coverage analysis'
        },
        makemehapi: {
            url: 'https://github.com/hapijs/makemehapi',
            description: 'Self guided workshops to teach you about hapi'
        },
        nipple: {
            url: 'https://github.com/hapijs/nipple',
            description: 'HTTP Client utilities'
        },
        qs: {
            url: 'https://github.com/hapijs/qs',
            description: 'A querystring parses with support for arrays and objects'
        },
        shot: {
            url: 'https://github.com/hapijs/shot',
            description: 'Injects a fake HTTP request/response into your node server logic'
        },
        topo: {
            url: 'https://github.com/hapijs/topo',
            description: 'Topological sorting with grouping support'
        }
    }
};
<commit_msg>plugins list: nipple -> wreck<commit_after>exports.categories = {
    'Authentication': {
        bell: {
            url: 'https://github.com/hapijs/bell',
            description: 'Third-party login plugin for hapi with built in Facebook, GitHub, Google, Twitter, Yahoo, Foursquare, and Windows Live support'
        },
        'hapi-auth-basic': {
            url: 'https://github.com/hapijs/hapi-auth-basic',
            description: 'An HTTP Basic authentication scheme'
        },
        'hapi-auth-bearer-token': {
            url: 'https://github.com/johnbrett/hapi-auth-bearer-token',
            description: 'A bearer token based authentication scheme'
        },
        'hapi-auth-cookie': {
            url: 'https://github.com/hapijs/hapi-auth-cookie',
            description: 'A cookie-based session authentication scheme'
        },
        'hapi-auth-hawk': {
            url :'https://github.com/hapijs/hapi-auth-hawk',
            description: 'Hawk authentication plugin'
        }
    },
    'Documentation': {
        lout: {
            url: 'https://github.com/hapijs/lout',
            description: 'A browsable documentation generator'
        }
    },
    'Logging/Metrics': {
        good: {
            url: 'https://github.com/hapijs/good',
            description: 'A logging plugin that supports output to console, file and udp/http endpoints'
        },
        'hapi-statsd': {
            url: 'https://github.com/mac-/hapi-statsd',
            description: 'Sends request round trip metrics to statsd'
        }
    },
    'Session': {
        crumb: {
            url: 'https://github.com/hapijs/crumb',
            description: 'CSRF crumb generation and validation for hapi'
        },
        yar: {
            url: 'https://github.com/hapijs/yar',
            description: 'A hapi session plugin and cookie jar'
        }
    },
    'Utility': {
        bassmaster: {
            url: 'https://github.com/hapijs/bassmaster',
            description: 'The batch endpoint makes it easy to combine multiple requests into a single one'
        },
        halacious: {
            url: 'https://github.com/bleupen/halacious',
            description: 'A HAL processor for hapi servers'
        },
        'hapi-mongoose-db-connector' : {
            url: 'https://github.com/codedoctor/hapi-mongoose-db-connector',
            description: 'hapi plugin that connects to mongodb for mongoose apps'
        },
        'hapi-route-directory': {
            url: 'https://github.com/clarkie/hapi-route-directory',
            description: 'A lightweight json route directory'
        },
        'hapi-routes-status' : {
          url: 'https://github.com/codedoctor/hapi-routes-status',
          description: 'Exposes a status route for your node.js/hapi projects.'
        },
        poop: {
            url: 'https://github.com/hapijs/poop',
            description: 'Plugin for taking a process dump and cleaning up after an uncaught exception'
        },
        reptile: {
            url: 'https://github.com/hapijs/reptile',
            description: 'A plugin for creating a REPL'
        },
        scooter: {
            url: 'https://github.com/hapijs/scooter',
            description: 'User-agent information plugin'
        },
        tv: {
            url: 'https://github.com/hapijs/tv',
            description: 'Interactive debug console plugin for hapi servers'
        }
    },
    'The extended hapi universe': {
        boom: {
            url: 'https://github.com/hapijs/boom',
            description: 'HTTP-friendly error objects'
        },
        confidence: {
            url: 'https://github.com/hapijs/confidence',
            description: 'A configuration document format, an API, and a foundation for A/B testing'
        },
        faketoe: {
            url: 'https://github.com/hapijs/faketoe',
            description: 'An XML to JSON converter'
        },
        hoek: {
            url: 'https://github.com/hapijs/hoek',
            description: 'General purpose node utilities'
        },
        joi: {
            url: 'https://github.com/hapijs/joi',
            description: 'Object schema description language and validator for JavaScript objects'
        },
        kilt: {
            url: 'https://github.com/hapijs/kilt',
            description: 'Combine multiple event emitters into a single emitter'
        },
        lab: {
            url: 'https://github.com/hapijs/lab',
            description: 'A simple testing utility with code coverage analysis'
        },
        makemehapi: {
            url: 'https://github.com/hapijs/makemehapi',
            description: 'Self guided workshops to teach you about hapi'
        },
        qs: {
            url: 'https://github.com/hapijs/qs',
            description: 'A querystring parses with support for arrays and objects'
        },
        shot: {
            url: 'https://github.com/hapijs/shot',
            description: 'Injects a fake HTTP request/response into your node server logic'
        },
        topo: {
            url: 'https://github.com/hapijs/topo',
            description: 'Topological sorting with grouping support'
        },
        wreck: {
            url: 'https://github.com/hapijs/wreck',
            description: 'HTTP Client utilities'
        }
    }
};
<|endoftext|>