<commit_before>/*
 * patron - The cleanest command framework for discord.js and eris.
 * Copyright (C) 2020  patron contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
"use strict";
const path = require("path");
const ArgumentPrecondition = require("./ArgumentPrecondition.js");
const Command = require("./Command.js");
const Group = require("./Group.js");
const Postcondition = require("./Postcondition.js");
const Precondition = require("./Precondition.js");
const TypeReader = require("./TypeReader.js");
const {invalidOptions} = require("../utils/structUtil.js");
const lib = require("../utils/libraryHandler.js");
const {RequireAllSync} = require("../utils/RequireAll.js");

class Registry {
  constructor(options = {}) {
    if(invalidOptions(options, true))
      throw ReferenceError("The Registry constructor requires an options object.");

    this.caseSensitive = options.caseSensitive ?? true;
    this.defaultReaders = options.defaultReaders ?? true;
    this.argumentPreconditions = new Map();
    this.commands = new Map();
    this.groups = new Map();
    this.postconditions = new Map();
    this.preconditions = new Map();
    this.typeReaders = new Map();
    this.prefixes = new Map();
    Registry.validate(this);

    if(this.defaultReaders) {
      const readers = RequireAllSync(path.join(__dirname, "../readers"));

      this.registerTypeReaders(readers.map(reader => reader instanceof TypeReader ? reader : reader(this)));
    }
  }

  getGroupedCommands(groupName) {
    if(!this.getGroup(groupName))
      throw ReferenceError(`The '${groupName}' Group isn't registered.`);

    const commands = [];
    const added = [];

    for(const cmd of this.commands.values()) {
      if(!added.includes(cmd.names[0]) && this.equals(cmd.group, groupName)) {
        commands.push(cmd);
        added.push(cmd.names[0]);
      }
    }

    return commands;
  }

  getArgumentPrecondition(name) {
    return this.argumentPreconditions.get(this.toLowerCase(name));
  }

  getCommand(name) {
    return this.commands.get(this.toLowerCase(name));
  }

  getGroup(name) {
    return this.groups.get(this.toLowerCase(name));
  }

  getPostcondition(name) {
    return this.postconditions.get(this.toLowerCase(name));
  }

  getPrecondition(name) {
    return this.preconditions.get(this.toLowerCase(name));
  }

  getTypeReader(type) {
    return this.typeReaders.get(this.toLowerCase(type));
  }

  registerPrefixes(prefixes, guildId = "global") {
    const existing = this.prefixes.get(guildId);
    const results = existing?.concat(prefixes) ?? prefixes;

    this.prefixes.set(guildId, results.filter((val, i) => results.indexOf(val) === i));

    return this;
  }

  unregisterPrefixes(prefixes, guildId = "global") {
    const existing = this.prefixes.get(guildId);

    if(existing == null)
      return;
    else if(prefixes === "all")
      this.prefixes.delete(guildId);
    else
      this.prefixes.set(guildId, existing.filter(val => !prefixes.includes(val)));

    return this;
  }

  registerArgumentPreconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof ArgumentPrecondition)))
      throw TypeReader("Registry#registerArgumentPreconditions() only accepts an array of ArgumentPreconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.argumentPreconditions.has(name))
        throw ReferenceError(`Another ArgumentPrecondition already has the '${name}' name.`);

      this.argumentPreconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterArgumentPreconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterArgumentPreconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.arguments.some(a => a.preconditions.some(p => this.equals(p, names[i]))))
          throw ReferenceError(`The '${names[i]}' ArgumentPrecondition is in use and can't be unregistered.`);
      }

      const deleted = this.argumentPreconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`An ArgumentPrecondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerCommands(commands) {
    if(typeof commands === "string")
      commands = RequireAllSync(commands);

    if(!Array.isArray(commands) || commands.some(c => !(c instanceof Command)))
      throw TypeError("Registry#registerCommands only accepts an array of Commands.");

    for(let i = 0; i < commands.length; i++) {
      if(commands[i].names.length === 0)
        throw ReferenceError("Commands must have at least one name.");

      const names = commands[i].names.map(name => this.toLowerCase(name));
      let res;

      for(let j = 0; j < names.length; j++) {
        if(this.commands.has(names[j]))
          throw ReferenceError(`Another Command already has the '${names[j]}' name.`);
      }

      if((res = this.validateConditions(commands[i])) != null)
        throw ReferenceError(res);
      else if((res = this.validateCommand(commands[i])) != null)
        throw ReferenceError(res);

      for(let j = 0; j < names.length; j++)
        this.commands.set(names[j], commands[i]);
    }

    return this;
  }

  unregisterCommands(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterCommands() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const command = this.getCommand(names[i]);

      if(command == null)
        throw ReferenceError(`A Command with the '${names[i]}' name hasn't been registered.`);

      const list = command.names.map(name => this.toLowerCase(name));

      for(let j = 0; j < list.length; j++)
        this.commands.delete(list[j]);

      return true;
    }

    return this;
  }

  registerGroups(groups) {
    if(typeof groups === "string")
      groups = RequireAllSync(groups);

    if(!Array.isArray(groups) || groups.some(c => !(c instanceof Group)))
      throw TypeError("Registry#registerGroups only accepts an array of Groups.");

    for(let i = 0; i < groups.length; i++) {
      const name = this.toLowerCase(groups[i].name);
      let res;

      if(this.groups.has(name))
        throw ReferenceError(`Another Group already has the '${name}' name.`);
      else if((res = this.validateConditions(groups[i])) != null)
        throw ReferenceError(res);

      this.groups.set(name, groups[i]);
    }

    return this;
  }

  unregisterGroups(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterGroups() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(this.equals(command.group, names[i]))
          throw ReferenceError(`The ${names[i]} group is in use and can't be unregistered.`);
      }

      const deleted = this.groups.delete(name);

      if(!deleted)
        throw ReferenceError(`A Group with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerPostconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof Postcondition)))
      throw TypeError("Registry#registerPostconditions only accepts an array of Postconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.postconditions.has(name))
        throw ReferenceError(`Another Postcondition already has the '${name}' name.`);

      this.postconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterPostconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterPostconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.postconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Postcondition is in use and can't be unregistered.`);
      }

      for(const group of this.group.values()) {
        if(group.postconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Postcondition is in use and can't be unregistered.`);
      }

      const deleted = this.postconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`A Postcondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerPreconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof Precondition)))
      throw TypeError("Registry#registerPreconditions only accepts an array of Preconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.preconditions.has(name))
        throw ReferenceError(`Another Precondition already has the '${name}' name.`);

      this.preconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterPreconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterPreconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.preconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Precondition is in use and can't be unregistered.`);
      }

      for(const group of this.group.values()) {
        if(group.preconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Precondition is in use and can't be unregistered.`);
      }

      const deleted = this.preconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`A Precondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerTypeReaders(readers) {
    if(typeof readers === "string")
      readers = RequireAllSync(readers);

    if(!Array.isArray(readers) || readers.some(r => !(r instanceof TypeReader)))
      throw TypeError("Registry#registerTypeReaders() only accepts an array of TypeReaders.");

    for(let i = 0; i < readers.length; i++) {
      const type = this.toLowerCase(readers[i].type);
      const existing = this.typeReaders.get(type);

      if(existing == null || existing.default)
        this.typeReaders.set(type, readers[i]);
      else
        throw ReferenceError(`Another TypeReader already has the '${type}' type.`);
    }

    return this;
  }

  unregisterTypeReaders(types) {
    if(!Array.isArray(types) || types.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterTypeReaders() only accepts an array of strings.");

    for(let i = 0; i < types.length; i++) {
      const type = this.toLowerCase(types[i]);

      for(const command of this.commands.values()) {
        if(command.arguments.some(a => this.equals(a.type, types[i])))
          throw ReferenceError(`The '${types[i]}' TypeReader is in use and can't be unregistered.`);
      }

      if(!this.typeReaders.delete(type))
        throw ReferenceError(`A TypeReader with the '${types[i]}' type hasn't been registered.`);
    }

    return this;
  }

  validateConditions(obj) {
    if(obj.preconditionOptions.length > obj.preconditions.length)
      return "Too many Precondition options were provided.";
    else if(obj.postconditionOptions.length > obj.postconditions.length)
      return "Too many Postcondition options were provided.";

    for(let i = 0; i < obj.preconditions.length; i++) {
      if(!this.getPrecondition(obj.preconditions[i]))
        return `The '${obj.preconditions[i]}' Precondition isn't registered.`;
    }

    for(let i = 0; i < obj.postconditions.length; i++) {
      if(!this.getPostcondition(obj.postconditions[i]))
        return `The '${obj.postconditions[i]}' Postcondition isn't registered.`;
    }
  }

  validateCommand(command) {
    let perm;

    if(command.group != null && !this.getGroup(command.group))
      return `The '${command.group}' Group isn't registered.`;
    else if(command.clientPermissions.some(p => !lib.isPerm(p) && (perm = p))
        || command.memberPermissions.some(p => !lib.isPerm(p) && (perm = p)))
      return `The '${perm}' permission doesn't exist.`;

    for(let i = 0; i < command.arguments.length; i++) {
      const arg = command.arguments[i];

      if(!this.getTypeReader(arg.type))
        return `The '${arg.type}' TypeReader isn't registered.`;
      else if(arg.infinite && arg.remainder)
        return "An Argument may not be both infinite and a remainder.";
      else if((arg.infinite || arg.remainder) && i !== command.arguments.length - 1)
        return "Only the last Argument may be infinite or a remainder.";
      else if(arg.preconditionOptions.length > arg.preconditions.length)
        return "Too many ArgumentPrecondition options provided.";

      for(let j = 0; j < arg.preconditions.length; j++) {
        if(!this.getArgumentPrecondition(arg.preconditions[j]))
          return `The '${arg.preconditions[j]}' ArgumentPrecondition isn't registered.`;
      }

      for(let j = i + 1; j < command.arguments.length; j++) {
        if(command.arguments[j].key === arg.key)
          return `The '${arg.key}' Argument key is already taken.`;
      }
    }
  }

  equals(cero, uno) {
    if(this.caseSensitive)
      return cero === uno;

    return cero.toLowerCase() === uno.toLowerCase();
  }

  toLowerCase(string) {
    if(this.caseSensitive)
      return string;

    return string.toLowerCase();
  }

  static validate(registry) {
    if(typeof registry.caseSensitive !== "boolean")
      throw TypeError("Registry#caseSensitive must be a boolean.");
    else if(typeof registry.defaultReaders !== "boolean")
      throw TypeError("Registry#defaultReaders must be a boolean.");
  }
}

module.exports = Registry;
<commit_msg>Fix bug in unregistering prefixes<commit_after>/*
 * patron - The cleanest command framework for discord.js and eris.
 * Copyright (C) 2020  patron contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
"use strict";
const path = require("path");
const ArgumentPrecondition = require("./ArgumentPrecondition.js");
const Command = require("./Command.js");
const Group = require("./Group.js");
const Postcondition = require("./Postcondition.js");
const Precondition = require("./Precondition.js");
const TypeReader = require("./TypeReader.js");
const {invalidOptions} = require("../utils/structUtil.js");
const lib = require("../utils/libraryHandler.js");
const {RequireAllSync} = require("../utils/RequireAll.js");

class Registry {
  constructor(options = {}) {
    if(invalidOptions(options, true))
      throw ReferenceError("The Registry constructor requires an options object.");

    this.caseSensitive = options.caseSensitive ?? true;
    this.defaultReaders = options.defaultReaders ?? true;
    this.argumentPreconditions = new Map();
    this.commands = new Map();
    this.groups = new Map();
    this.postconditions = new Map();
    this.preconditions = new Map();
    this.typeReaders = new Map();
    this.prefixes = new Map();
    Registry.validate(this);

    if(this.defaultReaders) {
      const readers = RequireAllSync(path.join(__dirname, "../readers"));

      this.registerTypeReaders(readers.map(reader => reader instanceof TypeReader ? reader : reader(this)));
    }
  }

  getGroupedCommands(groupName) {
    if(!this.getGroup(groupName))
      throw ReferenceError(`The '${groupName}' Group isn't registered.`);

    const commands = [];
    const added = [];

    for(const cmd of this.commands.values()) {
      if(!added.includes(cmd.names[0]) && this.equals(cmd.group, groupName)) {
        commands.push(cmd);
        added.push(cmd.names[0]);
      }
    }

    return commands;
  }

  getArgumentPrecondition(name) {
    return this.argumentPreconditions.get(this.toLowerCase(name));
  }

  getCommand(name) {
    return this.commands.get(this.toLowerCase(name));
  }

  getGroup(name) {
    return this.groups.get(this.toLowerCase(name));
  }

  getPostcondition(name) {
    return this.postconditions.get(this.toLowerCase(name));
  }

  getPrecondition(name) {
    return this.preconditions.get(this.toLowerCase(name));
  }

  getTypeReader(type) {
    return this.typeReaders.get(this.toLowerCase(type));
  }

  registerPrefixes(prefixes, guildId = "global") {
    const existing = this.prefixes.get(guildId);
    const results = existing?.concat(prefixes) ?? prefixes;

    this.prefixes.set(guildId, results.filter((val, i) => results.indexOf(val) === i));

    return this;
  }

  unregisterPrefixes(prefixes, guildId = "global") {
    const existing = this.prefixes.get(guildId);

    if(existing == null)
      return this;
    else if(prefixes === "all")
      this.prefixes.delete(guildId);
    else
      this.prefixes.set(guildId, existing.filter(val => !prefixes.includes(val)));

    return this;
  }

  registerArgumentPreconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof ArgumentPrecondition)))
      throw TypeReader("Registry#registerArgumentPreconditions() only accepts an array of ArgumentPreconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.argumentPreconditions.has(name))
        throw ReferenceError(`Another ArgumentPrecondition already has the '${name}' name.`);

      this.argumentPreconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterArgumentPreconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterArgumentPreconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.arguments.some(a => a.preconditions.some(p => this.equals(p, names[i]))))
          throw ReferenceError(`The '${names[i]}' ArgumentPrecondition is in use and can't be unregistered.`);
      }

      const deleted = this.argumentPreconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`An ArgumentPrecondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerCommands(commands) {
    if(typeof commands === "string")
      commands = RequireAllSync(commands);

    if(!Array.isArray(commands) || commands.some(c => !(c instanceof Command)))
      throw TypeError("Registry#registerCommands only accepts an array of Commands.");

    for(let i = 0; i < commands.length; i++) {
      if(commands[i].names.length === 0)
        throw ReferenceError("Commands must have at least one name.");

      const names = commands[i].names.map(name => this.toLowerCase(name));
      let res;

      for(let j = 0; j < names.length; j++) {
        if(this.commands.has(names[j]))
          throw ReferenceError(`Another Command already has the '${names[j]}' name.`);
      }

      if((res = this.validateConditions(commands[i])) != null)
        throw ReferenceError(res);
      else if((res = this.validateCommand(commands[i])) != null)
        throw ReferenceError(res);

      for(let j = 0; j < names.length; j++)
        this.commands.set(names[j], commands[i]);
    }

    return this;
  }

  unregisterCommands(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterCommands() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const command = this.getCommand(names[i]);

      if(command == null)
        throw ReferenceError(`A Command with the '${names[i]}' name hasn't been registered.`);

      const list = command.names.map(name => this.toLowerCase(name));

      for(let j = 0; j < list.length; j++)
        this.commands.delete(list[j]);

      return true;
    }

    return this;
  }

  registerGroups(groups) {
    if(typeof groups === "string")
      groups = RequireAllSync(groups);

    if(!Array.isArray(groups) || groups.some(c => !(c instanceof Group)))
      throw TypeError("Registry#registerGroups only accepts an array of Groups.");

    for(let i = 0; i < groups.length; i++) {
      const name = this.toLowerCase(groups[i].name);
      let res;

      if(this.groups.has(name))
        throw ReferenceError(`Another Group already has the '${name}' name.`);
      else if((res = this.validateConditions(groups[i])) != null)
        throw ReferenceError(res);

      this.groups.set(name, groups[i]);
    }

    return this;
  }

  unregisterGroups(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterGroups() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(this.equals(command.group, names[i]))
          throw ReferenceError(`The ${names[i]} group is in use and can't be unregistered.`);
      }

      const deleted = this.groups.delete(name);

      if(!deleted)
        throw ReferenceError(`A Group with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerPostconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof Postcondition)))
      throw TypeError("Registry#registerPostconditions only accepts an array of Postconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.postconditions.has(name))
        throw ReferenceError(`Another Postcondition already has the '${name}' name.`);

      this.postconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterPostconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterPostconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.postconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Postcondition is in use and can't be unregistered.`);
      }

      for(const group of this.group.values()) {
        if(group.postconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Postcondition is in use and can't be unregistered.`);
      }

      const deleted = this.postconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`A Postcondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerPreconditions(conditions) {
    if(typeof conditions === "string")
      conditions = RequireAllSync(conditions);

    if(!Array.isArray(conditions) || conditions.some(c => !(c instanceof Precondition)))
      throw TypeError("Registry#registerPreconditions only accepts an array of Preconditions.");

    for(let i = 0; i < conditions.length; i++) {
      const name = this.toLowerCase(conditions[i].name);

      if(this.preconditions.has(name))
        throw ReferenceError(`Another Precondition already has the '${name}' name.`);

      this.preconditions.set(name, conditions[i]);
    }

    return this;
  }

  unregisterPreconditions(names) {
    if(!Array.isArray(names) || names.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterPreconditions() only accepts an array of strings.");

    for(let i = 0; i < names.length; i++) {
      const name = this.toLowerCase(names[i]);

      for(const command of this.commands.values()) {
        if(command.preconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Precondition is in use and can't be unregistered.`);
      }

      for(const group of this.group.values()) {
        if(group.preconditions.some(p => this.equals(p, names[i])))
          throw ReferenceError(`The '${names[i]}' Precondition is in use and can't be unregistered.`);
      }

      const deleted = this.preconditions.delete(name);

      if(!deleted)
        throw ReferenceError(`A Precondition with the '${names[i]}' name hasn't been registered.`);
    }

    return this;
  }

  registerTypeReaders(readers) {
    if(typeof readers === "string")
      readers = RequireAllSync(readers);

    if(!Array.isArray(readers) || readers.some(r => !(r instanceof TypeReader)))
      throw TypeError("Registry#registerTypeReaders() only accepts an array of TypeReaders.");

    for(let i = 0; i < readers.length; i++) {
      const type = this.toLowerCase(readers[i].type);
      const existing = this.typeReaders.get(type);

      if(existing == null || existing.default)
        this.typeReaders.set(type, readers[i]);
      else
        throw ReferenceError(`Another TypeReader already has the '${type}' type.`);
    }

    return this;
  }

  unregisterTypeReaders(types) {
    if(!Array.isArray(types) || types.some(item => typeof item !== "string"))
      throw TypeError("Registry#unregisterTypeReaders() only accepts an array of strings.");

    for(let i = 0; i < types.length; i++) {
      const type = this.toLowerCase(types[i]);

      for(const command of this.commands.values()) {
        if(command.arguments.some(a => this.equals(a.type, types[i])))
          throw ReferenceError(`The '${types[i]}' TypeReader is in use and can't be unregistered.`);
      }

      if(!this.typeReaders.delete(type))
        throw ReferenceError(`A TypeReader with the '${types[i]}' type hasn't been registered.`);
    }

    return this;
  }

  validateConditions(obj) {
    if(obj.preconditionOptions.length > obj.preconditions.length)
      return "Too many Precondition options were provided.";
    else if(obj.postconditionOptions.length > obj.postconditions.length)
      return "Too many Postcondition options were provided.";

    for(let i = 0; i < obj.preconditions.length; i++) {
      if(!this.getPrecondition(obj.preconditions[i]))
        return `The '${obj.preconditions[i]}' Precondition isn't registered.`;
    }

    for(let i = 0; i < obj.postconditions.length; i++) {
      if(!this.getPostcondition(obj.postconditions[i]))
        return `The '${obj.postconditions[i]}' Postcondition isn't registered.`;
    }
  }

  validateCommand(command) {
    let perm;

    if(command.group != null && !this.getGroup(command.group))
      return `The '${command.group}' Group isn't registered.`;
    else if(command.clientPermissions.some(p => !lib.isPerm(p) && (perm = p))
        || command.memberPermissions.some(p => !lib.isPerm(p) && (perm = p)))
      return `The '${perm}' permission doesn't exist.`;

    for(let i = 0; i < command.arguments.length; i++) {
      const arg = command.arguments[i];

      if(!this.getTypeReader(arg.type))
        return `The '${arg.type}' TypeReader isn't registered.`;
      else if(arg.infinite && arg.remainder)
        return "An Argument may not be both infinite and a remainder.";
      else if((arg.infinite || arg.remainder) && i !== command.arguments.length - 1)
        return "Only the last Argument may be infinite or a remainder.";
      else if(arg.preconditionOptions.length > arg.preconditions.length)
        return "Too many ArgumentPrecondition options provided.";

      for(let j = 0; j < arg.preconditions.length; j++) {
        if(!this.getArgumentPrecondition(arg.preconditions[j]))
          return `The '${arg.preconditions[j]}' ArgumentPrecondition isn't registered.`;
      }

      for(let j = i + 1; j < command.arguments.length; j++) {
        if(command.arguments[j].key === arg.key)
          return `The '${arg.key}' Argument key is already taken.`;
      }
    }
  }

  equals(cero, uno) {
    if(this.caseSensitive)
      return cero === uno;

    return cero.toLowerCase() === uno.toLowerCase();
  }

  toLowerCase(string) {
    if(this.caseSensitive)
      return string;

    return string.toLowerCase();
  }

  static validate(registry) {
    if(typeof registry.caseSensitive !== "boolean")
      throw TypeError("Registry#caseSensitive must be a boolean.");
    else if(typeof registry.defaultReaders !== "boolean")
      throw TypeError("Registry#defaultReaders must be a boolean.");
  }
}

module.exports = Registry;
<|endoftext|>


<commit_before>module.exports = {extends: ['@commitlint/config-angular']}
<commit_msg>Remove commitlint config file<commit_after><|endoftext|>


<commit_before>
/**
 * @constructor Observable
 * @see {@link https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md}
 */

 /**
 * The forEach method triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
 * @name forEach
 * @memberof Observable.prototype
 * @function
 * @arg {?Observable~onNextCallback} onNext a callback that accepts the next value in the stream of values
 * @arg {?Observable~onErrorCallback} onError a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
 * @arg {?Observable~onCompletedCallback} onCompleted a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~onNextCallback} will not receive any more values
 * @return {Subscription}
 */

/**
 * This callback accepts a value that was emitted while evaluating the operation underlying the {@link Observable} stream.
 * @callback Observable~onNextCallback
 * @param {Object} value the value that was emitted while evaluating the operation underlying the {@link Observable}
 */

/**
 * This callback accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream. When this callback is invoked, the {@link Observable} stream ends and no more values will be received by the {@link Observable~onNextCallback}.
 * @callback Observable~onErrorCallback
 * @param {Error} error the error that occurred while evaluating the operation underlying the {@link Observable}
 */

 /**
 * This callback is invoked when the {@link Observable} stream ends. When this callback is invoked the {@link Observable} stream has ended, and therefore the {@link Observable~onNextCallback} will not receive any more values.
 * @callback Observable~onCompletedCallback
 */

/**
 * @constructor Subscription
 * @see {@link https://github.com/Reactive-Extensions/RxJS/tree/master/doc}
 */

/**
 * @name dispose
 * @method
 * @memberof Subscription.prototype
 */
<commit_msg>Update Observable.js<commit_after>
/**
 * @constructor Observable
 */

 /**
 * The forEach method is a synonym for {@link Observable.prototype.subscribe} and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
 * @name forEach
 * @memberof Observable.prototype
 * @function
 * @arg {?Observable~onNextCallback} onNext a callback that accepts the next value in the stream of values
 * @arg {?Observable~onErrorCallback} onError a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
 * @arg {?Observable~onCompletedCallback} onCompleted a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~onNextCallback} will not receive any more values
 * @return {Subscription}
 */

 /**
 * The subscribe method is a synonym for {@link Observable.prototype.forEach} and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
 * @name forEach
 * @memberof Observable.prototype
 * @function
 * @arg {?Observable~onNextCallback} onNext a callback that accepts the next value in the stream of values
 * @arg {?Observable~onErrorCallback} onError a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
 * @arg {?Observable~onCompletedCallback} onCompleted a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~onNextCallback} will not receive any more values
 * @return {Subscription}
 */

/**
 * This callback accepts a value that was emitted while evaluating the operation underlying the {@link Observable} stream.
 * @callback Observable~onNextCallback
 * @param {Object} value the value that was emitted while evaluating the operation underlying the {@link Observable}
 */

/**
 * This callback accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream. When this callback is invoked, the {@link Observable} stream ends and no more values will be received by the {@link Observable~onNextCallback}.
 * @callback Observable~onErrorCallback
 * @param {Error} error the error that occurred while evaluating the operation underlying the {@link Observable}
 */

 /**
 * This callback is invoked when the {@link Observable} stream ends. When this callback is invoked the {@link Observable} stream has ended, and therefore the {@link Observable~onNextCallback} will not receive any more values.
 * @callback Observable~onCompletedCallback
 */

/**
 * @constructor Subscription
 * @see {@link https://github.com/Reactive-Extensions/RxJS/tree/master/doc}
 */

/**
 * When this method is called on the Subscription, the Observable that created the Subscription will stop sending values to the callbacks passed when the Subscription was created.
 * @name dispose
 * @method
 * @memberof Subscription.prototype
 */
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')
const {pipe} = require('mississippi')

const {bytesLimit, computeDigest} = require('../../util/streams')
const {unarchive, isArchive} = require('../../util/archive')
const {createTempDirectory} = require('../../util/tmpdir')

const {getDigestString} = require('../digest')

async function downloadFile(token, options) {
  const tmpDirectory = await createTempDirectory()
  const filePath = path.join(tmpDirectory.path, token.fileName)

  return new Promise((resolve, reject) => {
    let digest
    let fileSize

    const onDigest = result => {
      digest = getDigestString(result.digest, options.digestAlgorithm)
      fileSize = result.bodyLength
    }

    pipe(
      token.response.body,
      computeDigest(options.digestAlgorithm, onDigest),
      bytesLimit(100 * 1024 * 1024),
      fs.createWriteStream(filePath),
      err => {
        if (err) return reject(err)
        resolve({filePath, digest, fileSize})
      }
    )
  })
}

async function parseArchive(token, options) {
  if (token.parsed) return false
  if (!isArchive(token)) return false

  const {filePath, digest} = await downloadFile(token, options)
  const location = await unarchive(filePath, path.dirname(filePath))

  token.digest = digest
  token.type = 'archive'
  token.temporary = true
  token.children = [{
    inputType: 'path',
    location,
    temporary: true,
    filePath: token.fileName
  }]
  token.parsed = true
}

module.exports = parseArchive
<commit_msg>Remove temporary on url archives<commit_after>'use strict'

const fs = require('fs')
const path = require('path')
const {pipe} = require('mississippi')

const {bytesLimit, computeDigest} = require('../../util/streams')
const {unarchive, isArchive} = require('../../util/archive')
const {createTempDirectory} = require('../../util/tmpdir')

const {getDigestString} = require('../digest')

async function downloadFile(token, options) {
  const tmpDirectory = await createTempDirectory()
  const filePath = path.join(tmpDirectory.path, token.fileName)

  return new Promise((resolve, reject) => {
    let digest
    let fileSize

    const onDigest = result => {
      digest = getDigestString(result.digest, options.digestAlgorithm)
      fileSize = result.bodyLength
    }

    pipe(
      token.response.body,
      computeDigest(options.digestAlgorithm, onDigest),
      bytesLimit(100 * 1024 * 1024),
      fs.createWriteStream(filePath),
      err => {
        if (err) return reject(err)
        resolve({filePath, digest, fileSize})
      }
    )
  })
}

async function parseArchive(token, options) {
  if (token.parsed) return false
  if (!isArchive(token)) return false

  const {filePath, digest} = await downloadFile(token, options)
  const location = await unarchive(filePath, path.dirname(filePath))

  token.digest = digest
  token.type = 'archive'
  token.children = [{
    inputType: 'path',
    location,
    temporary: true,
    filePath: token.fileName
  }]
  token.parsed = true
}

module.exports = parseArchive
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const NO_MARKER = 0;
const IN_PROGRESS_MARKER = 1;
const DONE_MARKER = 2;
const DONE_MAYBE_ROOT_CYCLE_MARKER = 3;
const DONE_AND_ROOT_MARKER = 4;

class Node {
	constructor(item) {
		this.item = item;
		this.dependencies = new Set();
		this.marker = NO_MARKER;
		this.cycle = undefined;
		this.incoming = 0;
	}
}

class Cycle {
	constructor() {
		this.nodes = new Set();
	}
}

/**
 * @typedef {Object} StackEntry
 * @property {Node} node
 * @property {Node[]} openEdges
 */

/**
 * @template T
 * @param {Iterable<T>} items list of items
 * @param {function(T): Iterable<T>} getDependencies function to get dependencies of an item (items that are not in list are ignored)
 * @returns {Iterable<T>} graph roots of the items
 */
module.exports = (items, getDependencies) => {
	const itemToNode = new Map();
	for (const item of items) {
		const node = new Node(item);
		itemToNode.set(item, node);
	}

	// early exit when there is only a single item
	if (itemToNode.size <= 1) return items;

	// grab all the dependencies
	for (const node of itemToNode.values()) {
		for (const dep of getDependencies(node.item)) {
			const depNode = itemToNode.get(dep);
			if (depNode !== undefined) {
				node.dependencies.add(depNode);
			}
		}
	}

	// Set of current root modules
	// items will be removed if a new reference to it has been found
	/** @type {Set<Node>} */
	const roots = new Set();

	// Set of current cycles without references to it
	// cycles will be removed if a new reference to it has been found
	// that is not part of the cycle
	/** @type {Set<Cycle>} */
	const rootCycles = new Set();

	// For all non-marked nodes
	for (const selectedNode of itemToNode.values()) {
		if (selectedNode.marker === NO_MARKER) {
			// deep-walk all referenced modules
			// in a non-recursive way

			// start by entering the selected node
			selectedNode.marker = IN_PROGRESS_MARKER;

			// keep a stack to avoid recursive walk
			/** @type {StackEntry[]} */
			const stack = [
				{
					node: selectedNode,
					openEdges: Array.from(selectedNode.dependencies)
				}
			];

			// process the top item until stack is empty
			while (stack.length > 0) {
				const topOfStack = stack[stack.length - 1];

				// Are there still edges unprocessed in the current node?
				if (topOfStack.openEdges.length > 0) {
					// Process one dependency
					const dependency = topOfStack.openEdges.pop();
					switch (dependency.marker) {
						case NO_MARKER:
							// dependency has not be visited yet
							// mark it as in-progress and recurse
							stack.push({
								node: dependency,
								openEdges: Array.from(dependency.dependencies)
							});
							dependency.marker = IN_PROGRESS_MARKER;
							break;
						case IN_PROGRESS_MARKER: {
							// It's a in-progress cycle
							let cycle = dependency.cycle;
							if (!cycle) {
								cycle = new Cycle();
								cycle.nodes.add(dependency);
								dependency.cycle = cycle;
								// Assume this is a root cycle
								// It will removed if not on node leaving
								rootCycles.add(cycle);
							}
							// set cycle property for each node in the cycle
							// if nodes are already part of a cycle
							// we merge the cycles to a shared cycle
							for (
								let i = stack.length - 1;
								stack[i].node !== dependency;
								i--
							) {
								const node = stack[i].node;
								if (node.cycle) {
									if (node.cycle !== cycle) {
										// merge cycles
										rootCycles.delete(node.cycle);
										for (const cycleNode of node.cycle.nodes) {
											cycleNode.cycle = cycle;
											cycle.nodes.add(cycleNode);
										}
									}
								} else {
									node.cycle = cycle;
									cycle.nodes.add(node);
								}
							}
							// don't recurse into dependencies
							// these are already on the stack
							break;
						}
						case DONE_AND_ROOT_MARKER:
							// This node has be visited yet and is currently a root node
							// But as this is a new reference to the node
							// it's not really a root
							// so we have to convert it to a normal node
							dependency.marker = DONE_MARKER;
							roots.delete(dependency);
							break;
						case DONE_MAYBE_ROOT_CYCLE_MARKER:
							// This node has be visited yet and is currently part of a completed root cycle
							// we found a new reference to the cycle
							// so it's not really a root cycle
							// remove the cycle from the root cycles
							// and convert it to a normal node
							rootCycles.delete(dependency.cycle);
							dependency.marker = DONE_MARKER;
							break;
						// DONE_MARKER: nothing to do, don't recurse into dependencies
					}
				} else {
					// All dependencies of the current node has been visited
					// we leave the node
					stack.pop();
					const node = topOfStack.node;
					// If the leaving node is part of a cycle
					// and parent node is not part of the same cycle
					// we have a parent-child-relationship between these cycles
					// and remove the child cycle from the rootCycles
					const cycle = node.cycle;
					if (cycle) {
						if (stack.length > 0) {
							const parent = stack[stack.length - 1];
							if (parent.node.cycle !== cycle) {
								rootCycles.delete(cycle);
								node.marker = DONE_MARKER;
								continue;
							}
						}
						node.marker = DONE_MAYBE_ROOT_CYCLE_MARKER;
					} else {
						node.marker = DONE_MARKER;
					}
				}
			}
			if (selectedNode.marker === DONE_MARKER) {
				selectedNode.marker = DONE_AND_ROOT_MARKER;
				roots.add(selectedNode);
			}
		}
	}

	// Extract roots from root cycles
	// We take the nodes with most incoming edges
	// inside of the cycle
	for (const cycle of rootCycles) {
		let max = 0;
		const cycleRoots = new Set();
		const nodes = cycle.nodes;
		for (const node of nodes) {
			for (const dep of node.dependencies) {
				if (nodes.has(dep)) {
					dep.incoming++;
					if (dep.incoming < max) continue;
					if (dep.incoming > max) {
						cycleRoots.clear();
						max = dep.incoming;
					}
					cycleRoots.add(dep);
				}
			}
		}
		for (const cycleRoot of cycleRoots) {
			roots.add(cycleRoot);
		}
	}

	// When roots were found, return them
	if (roots.size > 0) {
		return Array.from(roots, r => r.item);
	} else {
		throw new Error("Implementation of findGraphRoots is broken");
	}
};
<commit_msg>algorithm improvements<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const NO_MARKER = 0;
const IN_PROGRESS_MARKER = 1;
const DONE_MARKER = 2;
const DONE_MAYBE_ROOT_CYCLE_MARKER = 3;
const DONE_AND_ROOT_MARKER = 4;

class Node {
	constructor(item) {
		this.item = item;
		this.dependencies = new Set();
		this.marker = NO_MARKER;
		this.cycle = undefined;
		this.incoming = 0;
	}
}

class Cycle {
	constructor() {
		this.nodes = new Set();
	}
}

/**
 * @typedef {Object} StackEntry
 * @property {Node} node
 * @property {Node[]} openEdges
 */

/**
 * @template T
 * @param {Iterable<T>} items list of items
 * @param {function(T): Iterable<T>} getDependencies function to get dependencies of an item (items that are not in list are ignored)
 * @returns {Iterable<T>} graph roots of the items
 */
module.exports = (items, getDependencies) => {
	const itemToNode = new Map();
	for (const item of items) {
		const node = new Node(item);
		itemToNode.set(item, node);
	}

	// early exit when there is only a single item
	if (itemToNode.size <= 1) return items;

	// grab all the dependencies
	for (const node of itemToNode.values()) {
		for (const dep of getDependencies(node.item)) {
			const depNode = itemToNode.get(dep);
			if (depNode !== undefined) {
				node.dependencies.add(depNode);
			}
		}
	}

	// Set of current root modules
	// items will be removed if a new reference to it has been found
	/** @type {Set<Node>} */
	const roots = new Set();

	// Set of current cycles without references to it
	// cycles will be removed if a new reference to it has been found
	// that is not part of the cycle
	/** @type {Set<Cycle>} */
	const rootCycles = new Set();

	// For all non-marked nodes
	for (const selectedNode of itemToNode.values()) {
		if (selectedNode.marker === NO_MARKER) {
			// deep-walk all referenced modules
			// in a non-recursive way

			// start by entering the selected node
			selectedNode.marker = IN_PROGRESS_MARKER;

			// keep a stack to avoid recursive walk
			/** @type {StackEntry[]} */
			const stack = [
				{
					node: selectedNode,
					openEdges: Array.from(selectedNode.dependencies)
				}
			];

			// process the top item until stack is empty
			while (stack.length > 0) {
				const topOfStack = stack[stack.length - 1];

				// Are there still edges unprocessed in the current node?
				if (topOfStack.openEdges.length > 0) {
					// Process one dependency
					const dependency = topOfStack.openEdges.pop();
					switch (dependency.marker) {
						case NO_MARKER:
							// dependency has not be visited yet
							// mark it as in-progress and recurse
							stack.push({
								node: dependency,
								openEdges: Array.from(dependency.dependencies)
							});
							dependency.marker = IN_PROGRESS_MARKER;
							break;
						case IN_PROGRESS_MARKER: {
							// It's a in-progress cycle
							let cycle = dependency.cycle;
							if (!cycle) {
								cycle = new Cycle();
								cycle.nodes.add(dependency);
								dependency.cycle = cycle;
							}
							// set cycle property for each node in the cycle
							// if nodes are already part of a cycle
							// we merge the cycles to a shared cycle
							for (
								let i = stack.length - 1;
								stack[i].node !== dependency;
								i--
							) {
								const node = stack[i].node;
								if (node.cycle) {
									if (node.cycle !== cycle) {
										// merge cycles
										for (const cycleNode of node.cycle.nodes) {
											cycleNode.cycle = cycle;
											cycle.nodes.add(cycleNode);
										}
									}
								} else {
									node.cycle = cycle;
									cycle.nodes.add(node);
								}
							}
							// don't recurse into dependencies
							// these are already on the stack
							break;
						}
						case DONE_AND_ROOT_MARKER:
							// This node has be visited yet and is currently a root node
							// But as this is a new reference to the node
							// it's not really a root
							// so we have to convert it to a normal node
							dependency.marker = DONE_MARKER;
							roots.delete(dependency);
							break;
						case DONE_MAYBE_ROOT_CYCLE_MARKER:
							// This node has be visited yet and
							// is maybe currently part of a completed root cycle
							// we found a new reference to the cycle
							// so it's not really a root cycle
							// remove the cycle from the root cycles
							// and convert it to a normal node
							rootCycles.delete(dependency.cycle);
							dependency.marker = DONE_MARKER;
							break;
						// DONE_MARKER: nothing to do, don't recurse into dependencies
					}
				} else {
					// All dependencies of the current node has been visited
					// we leave the node
					stack.pop();
					topOfStack.node.marker = DONE_MARKER;
				}
			}
			const cycle = selectedNode.cycle;
			if (cycle) {
				for (const node of cycle.nodes) {
					node.marker = DONE_MAYBE_ROOT_CYCLE_MARKER;
				}
				rootCycles.add(cycle);
			} else {
				selectedNode.marker = DONE_AND_ROOT_MARKER;
				roots.add(selectedNode);
			}
		}
	}

	// Extract roots from root cycles
	// We take the nodes with most incoming edges
	// inside of the cycle
	for (const cycle of rootCycles) {
		let max = 0;
		const cycleRoots = new Set();
		const nodes = cycle.nodes;
		for (const node of nodes) {
			for (const dep of node.dependencies) {
				if (nodes.has(dep)) {
					dep.incoming++;
					if (dep.incoming < max) continue;
					if (dep.incoming > max) {
						cycleRoots.clear();
						max = dep.incoming;
					}
					cycleRoots.add(dep);
				}
			}
		}
		for (const cycleRoot of cycleRoots) {
			roots.add(cycleRoot);
		}
	}

	// When roots were found, return them
	if (roots.size > 0) {
		return Array.from(roots, r => r.item);
	} else {
		throw new Error("Implementation of findGraphRoots is broken");
	}
};
<|endoftext|>


<commit_before>
module.exports = errorHandler

var cbCalled = false
  , log = require("npmlog")
  , npm = require("../npm.js")
  , rm = require("rimraf")
  , itWorked = false
  , path = require("path")
  , wroteLogFile = false
  , exitCode = 0

process.on("exit", function (code) {
  // console.error("exit", code)
  if (!npm.config.loaded) return
  if (code) itWorked = false
  if (itWorked) log.info("ok")
  else {
    if (!cbCalled) {
      log.error("", "cb() never called!")
    }

    if (wroteLogFile) {
      log.error("", [""
                ,"Additional logging details can be found in:"
                ,"    " + path.resolve("npm-debug.log")
                ].join("\n"))
      wroteLogFile = false
    }
    log.error("not ok", "code", code)
  }

  var doExit = npm.config.get("_exit")
  if (doExit) {
    // actually exit.
    if (exitCode === 0 && !itWorked) {
      exitCode = 1
    }
    if (exitCode !== 0) process.exit(exitCode)
  } else {
    itWorked = false // ready for next exit
  }
})

function exit (code, noLog) {
  exitCode = exitCode || code

  var doExit = npm.config.get("_exit")
  log.verbose("exit", [code, doExit])
  if (log.level === "silent") noLog = true

  if (code && !noLog) writeLogFile(reallyExit)
  else rm("npm-debug.log", function () { rm(npm.tmp, reallyExit) })

  function reallyExit() {
    // truncate once it's been written.
    log.record.length = 0

    itWorked = !code

    // just emit a fake exit event.
    // if we're really exiting, then let it exit on its own, so that
    // in-process stuff can finish or clean up first.
    if (!doExit) process.emit("exit", code)
  }
}

function errorHandler (er) {
  var printStack = false
  // console.error("errorHandler", er)
  if (!npm.config.loaded) {
    // logging won't work unless we pretend that it's ready
    er = er || new Error("Exit prior to config file resolving.")
    console.error(er.stack || er.message)
  }

  if (cbCalled) {
    er = er || new Error("Callback called more than once.")
  }

  cbCalled = true
  if (!er) return exit(0)
  if (typeof er === "string") {
    log.error("", er)
    return exit(1, true)
  } else if (!(er instanceof Error)) {
    log.error("weird error", er)
    return exit(1, true)
  }

  var m = er.code || er.message.match(/^(?:Error: )?(E[A-Z]+)/)
  if (m && !er.code) er.code = m

  switch (er.code) {
  case "ECONNREFUSED":
    log.error("", er)
    log.error("", ["\nIf you are behind a proxy, please make sure that the"
              ,"'proxy' config is set properly.  See: 'npm help config'"
              ].join("\n"))
    printStack = true
    break

  case "EACCES":
  case "EPERM":
    log.error("", er)
    log.error("", ["\nPlease try running this command again as root/Administrator."
              ].join("\n"))
    printStack = true
    break

  case "ELIFECYCLE":
    er.code = "ELIFECYCLE"
    log.error("", er.message)
    log.error("", ["","Failed at the "+er.pkgid+" "+er.stage+" script."
              ,"This is most likely a problem with the "+er.pkgname+" package,"
              ,"not with npm itself."
              ,"Tell the author that this fails on your system:"
              ,"    "+er.script
              ,"You can get their info via:"
              ,"    npm owner ls "+er.pkgname
              ,"There is likely additional logging output above."
              ].join("\n"))
    break

  case "EJSONPARSE":
    er.code = "EJSONPARSE"
    log.error("", er.message)
    log.error("", "File: "+er.file)
    log.error("", ["Failed to parse package.json data."
              ,"package.json must be actual JSON, not just JavaScript."
              ,"","This is not a bug in npm."
              ,"Tell the package author to fix their package.json file."
              ].join("\n"), "JSON.parse")
    break

  case "E404":
    er.code = "E404"
    if (er.pkgid && er.pkgid !== "-") {
      var msg = ["'"+er.pkgid+"' is not in the npm registry."
                ,"You should bug the author to publish it"]
      if (er.pkgid.match(/^node[\.\-]|[\.\-]js$/)) {
        var s = er.pkgid.replace(/^node[\.\-]|[\.\-]js$/g, "")
        if (s !== er.pkgid) {
          s = s.replace(/[^a-z0-9]/g, ' ')
          msg.push("\nMaybe try 'npm search " + s + "'")
        }
      }
      msg.push("\nNote that you can also install from a"
              ,"tarball, folder, or http url, or git url.")
      log.error("404", msg.join("\n"))
    }
    break

  case "EPUBLISHCONFLICT":
    er.code = "EPUBLISHCONFLICT"
    log.error("publish fail", ["Cannot publish over existing version."
              ,"Bump the 'version' field, set the --force flag, or"
              ,"    npm unpublish '"+er.pkgid+"'"
              ,"and try again"
              ].join("\n"))
    break

  case "EISGIT":
    er.code = "EISGIT"
    log.error("git", [er.message
              ,"    "+er.path
              ,"Refusing to remove it. Update manually,"
              ,"or move it out of the way first."
              ].join("\n"))
    break

  case "ECYCLE":
    er.code = "ECYCLE"
    log.error("cycle", [er.message
              ,"While installing: "+er.pkgid
              ,"Found a pathological dependency case that npm cannot solve."
              ,"Please report this to the package author."
              ].join("\n"))
    break

  case "EBADPLATFORM":
    er.code = "EBADPLATFORM"
    log.error("notsup", [er.message
              ,"Not compatible with your operating system or architecture: "+er.pkgid
              ,"Valid OS:    "+er.os.join(",")
              ,"Valid Arch:  "+er.cpu.join(",")
              ,"Actual OS:   "+process.platform
              ,"Actual Arch: "+process.arch
              ].join("\n"))
    break

  case "EEXIST":
    log.error([er.message
              ,"File exists: "+er.path
              ,"Move it away, and try again."].join("\n"))
    break

  case "ENEEDAUTH":
    log.error("need auth", [er.message
              ,"You need to authorize this machine using `npm adduser`"
              ].join("\n"))
    break

  case "EPEERINVALID":
    var peerErrors = Object.keys(er.peersDepending).map(function (peer) {
      return "Peer " + peer + " wants " + er.packageName + "@"
        + er.peersDepending[peer]
    })
    log.error("peerinvalid", [er.message].concat(peerErrors).join("\n"))
    break

  case "ENOTSUP":
    if (er.required) {
      log.error("notsup", [er.message
                ,"Not compatible with your version of node/npm: "+er.pkgid
                ,"Required: "+JSON.stringify(er.required)
                ,"Actual:   "
                +JSON.stringify({npm:npm.version
                                ,node:npm.config.get("node-version")})
                ].join("\n"))
      break
    } // else passthrough

  default:
    log.error("", er.stack || er.message || er)
    log.error("", ["If you need help, you may report this log at:"
                  ,"    <http://github.com/isaacs/npm/issues>"
                  ,"or email it to:"
                  ,"    <npm-@googlegroups.com>"
                  ].join("\n"))
    printStack = false
    break
  }

  var os = require("os")
  // just a line break
  console.error("")
  log.error("System", os.type() + " " + os.release())
  log.error("command", process.argv
            .map(JSON.stringify).join(" "))
  log.error("cwd", process.cwd())
  log.error("node -v", process.version)
  log.error("npm -v", npm.version)

  ; [ "file"
    , "path"
    , "type"
    , "syscall"
    , "fstream_path"
    , "fstream_unc_path"
    , "fstream_type"
    , "fstream_class"
    , "fstream_finish_call"
    , "fstream_linkpath"
    , "code"
    , "errno"
    , "stack"
    , "fstream_stack"
    ].forEach(function (k) {
      var v = er[k]
      if (k === "stack") {
        if (!printStack) return
        if (!v) v = er.message
      }
      if (!v) return
      if (k === "fstream_stack") v = v.join("\n")
      log.error(k, v)
    })

  exit(typeof er.errno === "number" ? er.errno : 1)
}

var writingLogFile = false
function writeLogFile (cb) {
  if (writingLogFile) return cb()
  writingLogFile = true
  wroteLogFile = true

  var fs = require("graceful-fs")
    , fstr = fs.createWriteStream("npm-debug.log")
    , util = require("util")
    , eol = process.platform === "win32" ? "\r\n" : "\n"
    , out = ""

  log.record.forEach(function (m) {
    var pref = [m.id, m.level]
    if (m.prefix) pref.push(m.prefix)
    pref = pref.join(' ')

    m.message.trim().split(/\r?\n/).map(function (line) {
      return (pref + ' ' + line).trim()
    }).forEach(function (line) {
      out += line + eol
    })
  })

  fstr.end(out)
  fstr.on("close", cb)
}
<commit_msg>Do not suggest publish -f on EPUBLISHCONFLICT<commit_after>
module.exports = errorHandler

var cbCalled = false
  , log = require("npmlog")
  , npm = require("../npm.js")
  , rm = require("rimraf")
  , itWorked = false
  , path = require("path")
  , wroteLogFile = false
  , exitCode = 0

process.on("exit", function (code) {
  // console.error("exit", code)
  if (!npm.config.loaded) return
  if (code) itWorked = false
  if (itWorked) log.info("ok")
  else {
    if (!cbCalled) {
      log.error("", "cb() never called!")
    }

    if (wroteLogFile) {
      log.error("", [""
                ,"Additional logging details can be found in:"
                ,"    " + path.resolve("npm-debug.log")
                ].join("\n"))
      wroteLogFile = false
    }
    log.error("not ok", "code", code)
  }

  var doExit = npm.config.get("_exit")
  if (doExit) {
    // actually exit.
    if (exitCode === 0 && !itWorked) {
      exitCode = 1
    }
    if (exitCode !== 0) process.exit(exitCode)
  } else {
    itWorked = false // ready for next exit
  }
})

function exit (code, noLog) {
  exitCode = exitCode || code

  var doExit = npm.config.get("_exit")
  log.verbose("exit", [code, doExit])
  if (log.level === "silent") noLog = true

  if (code && !noLog) writeLogFile(reallyExit)
  else rm("npm-debug.log", function () { rm(npm.tmp, reallyExit) })

  function reallyExit() {
    // truncate once it's been written.
    log.record.length = 0

    itWorked = !code

    // just emit a fake exit event.
    // if we're really exiting, then let it exit on its own, so that
    // in-process stuff can finish or clean up first.
    if (!doExit) process.emit("exit", code)
  }
}

function errorHandler (er) {
  var printStack = false
  // console.error("errorHandler", er)
  if (!npm.config.loaded) {
    // logging won't work unless we pretend that it's ready
    er = er || new Error("Exit prior to config file resolving.")
    console.error(er.stack || er.message)
  }

  if (cbCalled) {
    er = er || new Error("Callback called more than once.")
  }

  cbCalled = true
  if (!er) return exit(0)
  if (typeof er === "string") {
    log.error("", er)
    return exit(1, true)
  } else if (!(er instanceof Error)) {
    log.error("weird error", er)
    return exit(1, true)
  }

  var m = er.code || er.message.match(/^(?:Error: )?(E[A-Z]+)/)
  if (m && !er.code) er.code = m

  switch (er.code) {
  case "ECONNREFUSED":
    log.error("", er)
    log.error("", ["\nIf you are behind a proxy, please make sure that the"
              ,"'proxy' config is set properly.  See: 'npm help config'"
              ].join("\n"))
    printStack = true
    break

  case "EACCES":
  case "EPERM":
    log.error("", er)
    log.error("", ["\nPlease try running this command again as root/Administrator."
              ].join("\n"))
    printStack = true
    break

  case "ELIFECYCLE":
    er.code = "ELIFECYCLE"
    log.error("", er.message)
    log.error("", ["","Failed at the "+er.pkgid+" "+er.stage+" script."
              ,"This is most likely a problem with the "+er.pkgname+" package,"
              ,"not with npm itself."
              ,"Tell the author that this fails on your system:"
              ,"    "+er.script
              ,"You can get their info via:"
              ,"    npm owner ls "+er.pkgname
              ,"There is likely additional logging output above."
              ].join("\n"))
    break

  case "EJSONPARSE":
    er.code = "EJSONPARSE"
    log.error("", er.message)
    log.error("", "File: "+er.file)
    log.error("", ["Failed to parse package.json data."
              ,"package.json must be actual JSON, not just JavaScript."
              ,"","This is not a bug in npm."
              ,"Tell the package author to fix their package.json file."
              ].join("\n"), "JSON.parse")
    break

  case "E404":
    er.code = "E404"
    if (er.pkgid && er.pkgid !== "-") {
      var msg = ["'"+er.pkgid+"' is not in the npm registry."
                ,"You should bug the author to publish it"]
      if (er.pkgid.match(/^node[\.\-]|[\.\-]js$/)) {
        var s = er.pkgid.replace(/^node[\.\-]|[\.\-]js$/g, "")
        if (s !== er.pkgid) {
          s = s.replace(/[^a-z0-9]/g, ' ')
          msg.push("\nMaybe try 'npm search " + s + "'")
        }
      }
      msg.push("\nNote that you can also install from a"
              ,"tarball, folder, or http url, or git url.")
      log.error("404", msg.join("\n"))
    }
    break

  case "EPUBLISHCONFLICT":
    er.code = "EPUBLISHCONFLICT"
    log.error("publish fail", ["Cannot publish over existing version."
              ,"Update the 'version' field in package.json and try again."
              ,""
              ,"If the previous version was published in error, see:"
              ,"    npm help unpublish"
              ,""
              ,"To automatically increment version numbers, see:"
              ,"    npm help version"
              ].join("\n"))
    break

  case "EISGIT":
    er.code = "EISGIT"
    log.error("git", [er.message
              ,"    "+er.path
              ,"Refusing to remove it. Update manually,"
              ,"or move it out of the way first."
              ].join("\n"))
    break

  case "ECYCLE":
    er.code = "ECYCLE"
    log.error("cycle", [er.message
              ,"While installing: "+er.pkgid
              ,"Found a pathological dependency case that npm cannot solve."
              ,"Please report this to the package author."
              ].join("\n"))
    break

  case "EBADPLATFORM":
    er.code = "EBADPLATFORM"
    log.error("notsup", [er.message
              ,"Not compatible with your operating system or architecture: "+er.pkgid
              ,"Valid OS:    "+er.os.join(",")
              ,"Valid Arch:  "+er.cpu.join(",")
              ,"Actual OS:   "+process.platform
              ,"Actual Arch: "+process.arch
              ].join("\n"))
    break

  case "EEXIST":
    log.error([er.message
              ,"File exists: "+er.path
              ,"Move it away, and try again."].join("\n"))
    break

  case "ENEEDAUTH":
    log.error("need auth", [er.message
              ,"You need to authorize this machine using `npm adduser`"
              ].join("\n"))
    break

  case "EPEERINVALID":
    var peerErrors = Object.keys(er.peersDepending).map(function (peer) {
      return "Peer " + peer + " wants " + er.packageName + "@"
        + er.peersDepending[peer]
    })
    log.error("peerinvalid", [er.message].concat(peerErrors).join("\n"))
    break

  case "ENOTSUP":
    if (er.required) {
      log.error("notsup", [er.message
                ,"Not compatible with your version of node/npm: "+er.pkgid
                ,"Required: "+JSON.stringify(er.required)
                ,"Actual:   "
                +JSON.stringify({npm:npm.version
                                ,node:npm.config.get("node-version")})
                ].join("\n"))
      break
    } // else passthrough

  default:
    log.error("", er.stack || er.message || er)
    log.error("", ["If you need help, you may report this log at:"
                  ,"    <http://github.com/isaacs/npm/issues>"
                  ,"or email it to:"
                  ,"    <npm-@googlegroups.com>"
                  ].join("\n"))
    printStack = false
    break
  }

  var os = require("os")
  // just a line break
  console.error("")
  log.error("System", os.type() + " " + os.release())
  log.error("command", process.argv
            .map(JSON.stringify).join(" "))
  log.error("cwd", process.cwd())
  log.error("node -v", process.version)
  log.error("npm -v", npm.version)

  ; [ "file"
    , "path"
    , "type"
    , "syscall"
    , "fstream_path"
    , "fstream_unc_path"
    , "fstream_type"
    , "fstream_class"
    , "fstream_finish_call"
    , "fstream_linkpath"
    , "code"
    , "errno"
    , "stack"
    , "fstream_stack"
    ].forEach(function (k) {
      var v = er[k]
      if (k === "stack") {
        if (!printStack) return
        if (!v) v = er.message
      }
      if (!v) return
      if (k === "fstream_stack") v = v.join("\n")
      log.error(k, v)
    })

  exit(typeof er.errno === "number" ? er.errno : 1)
}

var writingLogFile = false
function writeLogFile (cb) {
  if (writingLogFile) return cb()
  writingLogFile = true
  wroteLogFile = true

  var fs = require("graceful-fs")
    , fstr = fs.createWriteStream("npm-debug.log")
    , util = require("util")
    , eol = process.platform === "win32" ? "\r\n" : "\n"
    , out = ""

  log.record.forEach(function (m) {
    var pref = [m.id, m.level]
    if (m.prefix) pref.push(m.prefix)
    pref = pref.join(' ')

    m.message.trim().split(/\r?\n/).map(function (line) {
      return (pref + ' ' + line).trim()
    }).forEach(function (line) {
      out += line + eol
    })
  })

  fstr.end(out)
  fstr.on("close", cb)
}
<|endoftext|>


<commit_before>//for rev. 74
var TEXT_COLOR = "#FFFFFF";
var TEXTURE_BACKGROUND_COLOR = "#00FF00";

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight-20);
document.body.appendChild(renderer.domElement);
//var control = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.AmbientLight(0x888888);
scene.add(light);
light = new THREE.DirectionalLight(0x888888, 1.0);
light.position.set(10, 10, 0);
scene.add(light);

var canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 32;
//canvas.width = 16;
//canvas.height = 16;
var xc = canvas.getContext("2d");
xc.fillStyle=TEXTURE_BACKGROUND_COLOR;
xc.fillRect(0, 0, canvas.width, canvas.height);
xc.fillStyle=TEXT_COLOR;
//macでない場合どうなる?
xc.font = "18pt ヒラギノ角ゴ";
xc.fillText("お茶", 0, 22);

//1. 全面にテクスチャーをはった場合
//
var geometry = new THREE.CylinderGeometry(10, 10, 32, 128);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({ map: texture });
//var material = end_material;
var obj = new THREE.Mesh(geometry, material);
obj.position.x -= 15;
obj.rotation.y = -Math.PI/6;
scene.add(obj);

//2. 平らな面はテクスチャをやめる
geometry = new THREE.CylinderGeometry(10, 10, 32, 128);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var side_material = new THREE.MeshPhongMaterial({ map: texture });
var end_material = new THREE.MeshPhongMaterial({ color: TEXTURE_BACKGROUND_COLOR });
material = new THREE.MeshFaceMaterial([side_material, end_material]);

var axes = buildAxes(10000);
scene.add(axes);

//後ろ radialSegments*2個は end_material
//それ以外は side_material
for(var i = 0; i < geometry.faces.length-geometry.parameters.radialSegments*2; i++){
    geometry.faces[i].materialIndex = 0;
}
for(var i = geometry.faces.length-geometry.parameters.radialSegments*2; i < geometry.faces.length; i++){
    geometry.faces[i].materialIndex = 1;
}
obj = new THREE.Mesh(geometry, material);

obj.position.x += 15;
obj.rotation.y = -Math.PI/6;
scene.add(obj);

camera.position.set(0, 40, 130);

function buttonPressed(b) {
  if (typeof(b) == "object") {
    return b.pressed;
  }
  return b == 1.0;
}

//ps3
var L1 = 4;
var R1 = 5;
var L2 = 6;
var R2 = 7;
var UP = 12;
var DOWN = 13;
var LEFT = 14;
var RIGHT = 15;
//id:

//sega
//up: axis5 -1
//down: axis5 1
//left: axis4 -1
//right: axis4 1
//x: b3
//y: b0
//z: b4
//a: b2
//b: b1
//c: b5
//mode: b8
//start: b9
//id: ca3-24
var unit_speed = 2;

var ps3 = {
    PREFIX: '54c-268-',
    handle: function(gp){
        var backward = gp.buttons[6].pressed;
        var forward = gp.buttons[7].pressed;
        var p = camera.position;
        if(buttonPressed(gp.buttons[LEFT])){
            camera.position.set(p.x-unit_speed, p.y, p.z)
        }
        if(buttonPressed(gp.buttons[RIGHT])){
            camera.position.set(p.x+unit_speed, p.y, p.z)
        }
        if(buttonPressed(gp.buttons[L2])){
            camera.position.set(p.x, p.y, p.z+unit_speed)
        }
        if(buttonPressed(gp.buttons[R2])){
            camera.position.set(p.x, p.y, p.z-unit_speed)
        }
        if(buttonPressed(gp.buttons[UP])){
            camera.position.set(p.x, p.y+unit_speed, p.z)
        }
        if(buttonPressed(gp.buttons[DOWN])){
            camera.position.set(p.x, p.y-unit_speed, p.z)
        }
    }
};

var sega = {
    PREFIX: 'ca3-24-',
    UP_DOWN_AXES: 5,
    LEFT_RIGHT_AXES: 4,
    X: 3,
    Y: 0,
    Z: 4,
    A: 2,
    B: 1,
    C: 5,
    MODE: 8,
    START: 9,

    up_pressed: function(gp){
        return Math.abs(gp.axis[UP_DOWN_AXES] - (-1)) < 0.001;
    },
    down_pressed: function(gp){
        return Math.abs(gp.axis[UP_DOWN_AXES] - (1)) < 0.001;
    },
    left_pressed: function(gp){
        return Math.abs(gp.axis[LEFT_RIGHT_AXES] - (-1)) < 0.001;
    },
    right_pressed: function(gp){
        return Math.abs(gp.axis[LEFT_RIGHT_AXES] - (1)) < 0.001;
    },
    x_pressed: function(gp){
        return gp.axis[X] == 1;
    },
    y_pressed: function(gp){
        return gp.axis[Y] == 1;
    },
    z_pressed: function(gp){
        return gp.axis[Z] == 1;
    },
    a_pressed: function(gp){
        return gp.axis[A] == 1;
    },
    b_pressed: function(gp){
        return gp.axis[B] == 1;
    },
    c_pressed: function(gp){
        return gp.axis[C] == 1;
    },

    //default handler
    handle: function(gp){
        if(this.left_pressed(gp)){
            camera.position.set(p.x-unit_speed, p.y, p.z)
        }
        if(this.right_pressed(gp)){
            camera.position.set(p.x+unit_speed, p.y, p.z)
        }
        if(this.up_pressed(gp)){
            camera.position.set(p.x, p.y+unit_speed, p.z)
        }
        if(this.down_pressed(gp)){
            camera.position.set(p.x, p.y-unit_speed, p.z)
        }
    }
};

function render() {
    var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
    if (!gamepads) {
        requestAnimationFrame(render);
        return;
    }
    // console.log("gamepads: "+gamepads)
    // var i;
    // for(i = 0; i < gamepads.length; i++){
    //     console.log("gp: " + i + " "+gamepads[i]);
    // }
    var gp = gamepads[0];
    if(gp == null){
        console.log("gamepads: press some button to start")
        requestAnimationFrame(render);
        return;
    }
    // var backward = gp.buttons[6].pressed;
    // var forward = gp.buttons[7].pressed;
    // var unit_speed = 2;
    // var p = camera.position;
    // if(buttonPressed(gp.buttons[LEFT])){
    //     camera.position.set(p.x-unit_speed, p.y, p.z)
    // }
    // if(buttonPressed(gp.buttons[RIGHT])){
    //     camera.position.set(p.x+unit_speed, p.y, p.z)
    // }
    // if(buttonPressed(gp.buttons[L2])){
    //     camera.position.set(p.x, p.y, p.z+unit_speed)
    // }
    // if(buttonPressed(gp.buttons[R2])){
    //     camera.position.set(p.x, p.y, p.z-unit_speed)
    // }
    // if(buttonPressed(gp.buttons[UP])){
    //     camera.position.set(p.x, p.y+unit_speed, p.z)
    // }
    // if(buttonPressed(gp.buttons[DOWN])){
    //     camera.position.set(p.x, p.y-unit_speed, p.z)
    // }

    if(gp.id.startsWith(ps3)){
        console.log('handle ps3');
        ps3.handle(gp);
    }
    else if(gp.id.startsWith(sega)){
        console.log('handle sega');
        sega.handle(gp);
    }
    //console.log(camera.position.x+","+camera.position.y+","+camera.position.z)
    requestAnimationFrame(render);
    renderer.render(scene, camera);
}
render();
<commit_msg>log gp id<commit_after>//for rev. 74
var TEXT_COLOR = "#FFFFFF";
var TEXTURE_BACKGROUND_COLOR = "#00FF00";

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight-20);
document.body.appendChild(renderer.domElement);
//var control = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.AmbientLight(0x888888);
scene.add(light);
light = new THREE.DirectionalLight(0x888888, 1.0);
light.position.set(10, 10, 0);
scene.add(light);

var canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 32;
//canvas.width = 16;
//canvas.height = 16;
var xc = canvas.getContext("2d");
xc.fillStyle=TEXTURE_BACKGROUND_COLOR;
xc.fillRect(0, 0, canvas.width, canvas.height);
xc.fillStyle=TEXT_COLOR;
//macでない場合どうなる?
xc.font = "18pt ヒラギノ角ゴ";
xc.fillText("お茶", 0, 22);

//1. 全面にテクスチャーをはった場合
//
var geometry = new THREE.CylinderGeometry(10, 10, 32, 128);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({ map: texture });
//var material = end_material;
var obj = new THREE.Mesh(geometry, material);
obj.position.x -= 15;
obj.rotation.y = -Math.PI/6;
scene.add(obj);

//2. 平らな面はテクスチャをやめる
geometry = new THREE.CylinderGeometry(10, 10, 32, 128);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var side_material = new THREE.MeshPhongMaterial({ map: texture });
var end_material = new THREE.MeshPhongMaterial({ color: TEXTURE_BACKGROUND_COLOR });
material = new THREE.MeshFaceMaterial([side_material, end_material]);

var axes = buildAxes(10000);
scene.add(axes);

//後ろ radialSegments*2個は end_material
//それ以外は side_material
for(var i = 0; i < geometry.faces.length-geometry.parameters.radialSegments*2; i++){
    geometry.faces[i].materialIndex = 0;
}
for(var i = geometry.faces.length-geometry.parameters.radialSegments*2; i < geometry.faces.length; i++){
    geometry.faces[i].materialIndex = 1;
}
obj = new THREE.Mesh(geometry, material);

obj.position.x += 15;
obj.rotation.y = -Math.PI/6;
scene.add(obj);

camera.position.set(0, 40, 130);

function buttonPressed(b) {
  if (typeof(b) == "object") {
    return b.pressed;
  }
  return b == 1.0;
}

//ps3
var L1 = 4;
var R1 = 5;
var L2 = 6;
var R2 = 7;
var UP = 12;
var DOWN = 13;
var LEFT = 14;
var RIGHT = 15;
//id:

//sega
//up: axis5 -1
//down: axis5 1
//left: axis4 -1
//right: axis4 1
//x: b3
//y: b0
//z: b4
//a: b2
//b: b1
//c: b5
//mode: b8
//start: b9
//id: ca3-24
var unit_speed = 2;

var ps3 = {
    PREFIX: '54c-268-',
    handle: function(gp){
        var backward = gp.buttons[6].pressed;
        var forward = gp.buttons[7].pressed;
        var p = camera.position;
        if(buttonPressed(gp.buttons[LEFT])){
            camera.position.set(p.x-unit_speed, p.y, p.z)
        }
        if(buttonPressed(gp.buttons[RIGHT])){
            camera.position.set(p.x+unit_speed, p.y, p.z)
        }
        if(buttonPressed(gp.buttons[L2])){
            camera.position.set(p.x, p.y, p.z+unit_speed)
        }
        if(buttonPressed(gp.buttons[R2])){
            camera.position.set(p.x, p.y, p.z-unit_speed)
        }
        if(buttonPressed(gp.buttons[UP])){
            camera.position.set(p.x, p.y+unit_speed, p.z)
        }
        if(buttonPressed(gp.buttons[DOWN])){
            camera.position.set(p.x, p.y-unit_speed, p.z)
        }
    }
};

var sega = {
    PREFIX: 'ca3-24-',
    UP_DOWN_AXES: 5,
    LEFT_RIGHT_AXES: 4,
    X: 3,
    Y: 0,
    Z: 4,
    A: 2,
    B: 1,
    C: 5,
    MODE: 8,
    START: 9,

    up_pressed: function(gp){
        return Math.abs(gp.axis[UP_DOWN_AXES] - (-1)) < 0.001;
    },
    down_pressed: function(gp){
        return Math.abs(gp.axis[UP_DOWN_AXES] - (1)) < 0.001;
    },
    left_pressed: function(gp){
        return Math.abs(gp.axis[LEFT_RIGHT_AXES] - (-1)) < 0.001;
    },
    right_pressed: function(gp){
        return Math.abs(gp.axis[LEFT_RIGHT_AXES] - (1)) < 0.001;
    },
    x_pressed: function(gp){
        return gp.axis[X] == 1;
    },
    y_pressed: function(gp){
        return gp.axis[Y] == 1;
    },
    z_pressed: function(gp){
        return gp.axis[Z] == 1;
    },
    a_pressed: function(gp){
        return gp.axis[A] == 1;
    },
    b_pressed: function(gp){
        return gp.axis[B] == 1;
    },
    c_pressed: function(gp){
        return gp.axis[C] == 1;
    },

    //default handler
    handle: function(gp){
        if(this.left_pressed(gp)){
            camera.position.set(p.x-unit_speed, p.y, p.z)
        }
        if(this.right_pressed(gp)){
            camera.position.set(p.x+unit_speed, p.y, p.z)
        }
        if(this.up_pressed(gp)){
            camera.position.set(p.x, p.y+unit_speed, p.z)
        }
        if(this.down_pressed(gp)){
            camera.position.set(p.x, p.y-unit_speed, p.z)
        }
    }
};

function render() {
    var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
    if (!gamepads) {
        requestAnimationFrame(render);
        return;
    }
    // console.log("gamepads: "+gamepads)
    // var i;
    // for(i = 0; i < gamepads.length; i++){
    //     console.log("gp: " + i + " "+gamepads[i]);
    // }
    var gp = gamepads[0];
    if(gp == null){
        console.log("gamepads: press some button to start")
        requestAnimationFrame(render);
        return;
    }
    // var backward = gp.buttons[6].pressed;
    // var forward = gp.buttons[7].pressed;
    // var unit_speed = 2;
    // var p = camera.position;
    // if(buttonPressed(gp.buttons[LEFT])){
    //     camera.position.set(p.x-unit_speed, p.y, p.z)
    // }
    // if(buttonPressed(gp.buttons[RIGHT])){
    //     camera.position.set(p.x+unit_speed, p.y, p.z)
    // }
    // if(buttonPressed(gp.buttons[L2])){
    //     camera.position.set(p.x, p.y, p.z+unit_speed)
    // }
    // if(buttonPressed(gp.buttons[R2])){
    //     camera.position.set(p.x, p.y, p.z-unit_speed)
    // }
    // if(buttonPressed(gp.buttons[UP])){
    //     camera.position.set(p.x, p.y+unit_speed, p.z)
    // }
    // if(buttonPressed(gp.buttons[DOWN])){
    //     camera.position.set(p.x, p.y-unit_speed, p.z)
    // }
    console.log('id: ' + gp.id);
    if(gp.id.startsWith(ps3)){
        console.log('handle ps3');
        ps3.handle(gp);
    }
    else if(gp.id.startsWith(sega)){
        console.log('handle sega');
        sega.handle(gp);
    }
    //console.log(camera.position.x+","+camera.position.y+","+camera.position.z)
    requestAnimationFrame(render);
    renderer.render(scene, camera);
}
render();
<|endoftext|>