<commit_before>var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var solc = require("solc");
var path = require("path");
var Exec = require("./exec");
var Pudding = require("ether-pudding");
var PuddingGenerator = require("ether-pudding/generator");
var ConfigurationError = require("./errors/configurationerror");
var CompileError = require("./errors/compileerror");
var DeployError = require("./errors/deployerror");
var graphlib = require("graphlib");
var Graph = require("graphlib").Graph;
var isAcyclic = require("graphlib/lib/alg").isAcyclic;
var postOrder = require("graphlib/lib/alg").postorder;

var Contracts = {
  account: null,

  get_account: function(config, callback) {
    var self = this;

    if (config.app.resolved.rpc.from != null) {
      this.account = config.app.resolved.rpc.from;
    }

    if (this.account != null) {
      return callback(null, this.account);
    }

    config.web3.eth.getAccounts(function(err, result) {
      if (err != null) return callback(err);

      self.account = result[0];
      callback(null, self.account);
    });
  },

  update_sources(config, callback) {
    var contract_names = Object.keys(config.contracts.classes);
    async.each(contract_names, function(name, done) {
      var contract = config.contracts.classes[name];
      fs.readFile(contract.file, {encoding: "utf8"}, function(err, body) {
        if (err) return done(err);

        contract.body = body;
        done();
      });
    }, callback);
  },

  compile_necessary(config, callback) {
    this.update_sources(config, () => {
      var contract_names = Object.keys(config.contracts.classes);

      var sources = {};
      var updated = {};
      var included = {};

      for (var i = 0; i < contract_names.length; i++) {
        var name = contract_names[i];
        var contract = config.contracts.classes[name];

        if (contract.source_modified_time > contract.compiled_time || config.argv.compileAll === true) {
          updated[name] = true;
        }
      }

      if (Object.keys(updated).length == 0 && config.argv.quietDeploy == null) {
        console.log("No contracts updated; skipping compilation.");
        return callback();
      }

      var dependsGraph = this.build_compile_dependency_graph(config, callback);

      if (dependsGraph == null) {
        return;
      }

      function is_updated(contract_name) {
        return updated[contract_name] === true;
      }

      function include_source_for(contract_name) {
        var contract = config.contracts.classes[contract_name];
        var source_path = contract.source.replace("./contracts/", "");

        if (sources[source_path] != null) {
          return;
        }

        var full_path = path.resolve(config.working_dir, contract.source)
        sources[source_path] = fs.readFileSync(full_path, {encoding: "utf8"});

        // For graph traversing
        included[contract_name] = true;
      }

      function walk_down(contract_name) {
        if (included[contract_name] === true) {
          return;
        }

        include_source_for(contract_name);

        var dependencies = dependsGraph.successors(contract_name);

        // console.log("At: " + contract_name);
        // console.log("   Dependencies: ", dependencies);

        if (dependencies.length > 0) {
          dependencies.forEach(walk_down);
        }
      }

      function walk_from(contract_name) {
        // if (included[contract_name] === true) {
        //   return;
        // }

        var ancestors = dependsGraph.predecessors(contract_name);
        var dependencies = dependsGraph.successors(contract_name);

        // console.log("At: " + contract_name);
        // console.log("   Ancestors: ", ancestors);
        // console.log("   Dependencies: ", dependencies);

        include_source_for(contract_name);

        if (ancestors.length > 0) {
          ancestors.forEach(walk_from);
        }

        if (dependencies.length > 0) {
          dependencies.forEach(walk_down);
        }
      }

      Object.keys(updated).forEach(walk_from);

      Object.keys(sources).sort().forEach(function(file_path) {
        if (config.argv.quietDeploy == null) {
          console.log("Compiling " + file_path + "...");
        }
      });

      var result = solc.compile({sources: sources}, 1);

      if (result.errors != null) {
        callback(new CompileError(result.errors.join()));
        return;
      }

      for (var i = 0; i < contract_names.length; i++) {
        var name = contract_names[i];
        var contract = config.contracts.classes[name];
        var compiled_contract = result.contracts[name];

        // If we didn't compile this contract this run, continue.
        if (compiled_contract == null) {
          continue;
        }

        contract.binary = compiled_contract.bytecode;
        contract.unlinked_binary = compiled_contract.bytecode;
        contract.abi = JSON.parse(compiled_contract.interface);
      }

      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        this.compile_necessary(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },

  createContractAndWait(config, contract_name) {
    var tx = {
      from: this.account,
      gas: config.app.resolved.rpc.gas || config.app.resolved.rpc.gasLimit,
      gasPrice: config.app.resolved.rpc.gasPrice, // 100 Shannon
      data: config.contracts.classes[contract_name].binary
    };

    return new Promise((accept, reject) => {
      config.web3.eth.sendTransaction(tx, (err, hash) => {
        if (err != null) {
          return reject(err);
        }

        var interval = setInterval(() => {
          config.web3.eth.getTransactionReceipt(hash, (err, receipt) => {
            if (err != null) {
              clearInterval(interval);

              return reject(err);
            }
            if (receipt != null) {
              if (config.argv.quietDeploy == null) {
                console.log("Deployed: "+contract_name+" to address: "+receipt.contractAddress);
              }

              accept({
                name: contract_name,
                address: receipt.contractAddress
              });
              clearInterval(interval);
            }
          });
        }, 500);
      })
    });
  },

  build_compile_dependency_graph(config, errorCallback) {
    if (config.argv.quietDeploy == null) {
      console.log("Checking sources...");
    }
    // Iterate through all the contracts looking for libraries and building a dependency graph
    var dependsGraph = new Graph();
    var contract_names = Object.keys(config.contracts.classes);
    for (var i = 0; i < contract_names.length; i++) {
      var name = contract_names[i]
      var contract = config.contracts.classes[name];

      if (contract == null) {
        errorCallback(new CompileError(`Could not find contract '${name}' for compiling. Check truffle.json.`));
        return null;
      }

      // Add the contract to the depend graph
      dependsGraph.setNode(name);

      // Find import statements and resolve those import paths, adding them to the graph.
      contract.body.split(/;|\n/).filter(function(line) {
        return line.indexOf("import") >= 0;
      }).forEach(function(line) {
        var regex = /import.*("|')([^"']+)("|')*/g;
        var match = regex.exec(line);

        if (match == null) return;

        var file = match[2];
        var dependency_name = path.basename(file, ".sol");

        if (!dependsGraph.hasEdge(name, dependency_name)) {
          dependsGraph.setEdge(name, dependency_name);
        }
      });
    }
    // Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
    if (!isAcyclic(dependsGraph))
    {
      console.log("ERROR: Cycles in dependency graph");
      dependsGraph.edges().forEach(function(o){
        console.log(o.v+" -- depends on --> "+o.w);
      });
      errorCallback(new CompileError(`Found cyclic dependencies. Adjust your import statements to remove cycles.`));
      return null;
    }
    return dependsGraph;
  },

  build_deploy_dependency_graph(config, errorCallback) {
    if (config.argv.quietDeploy == null) {
      console.log("Collecting dependencies...");
    }
    // Iterate through all the contracts looking for libraries and building a dependency graph
    var dependsGraph = new Graph();
    for (var i = 0; i < config.app.resolved.deploy.length; i++) {
      var key = config.app.resolved.deploy[i];
      var contract_class = config.contracts.classes[key];

      if (contract_class == null) {
        errorCallback(new DeployError(`Could not find contract '${key}' for linking. Check truffle.json.`));
        return null;
      }

      if (contract_class.binary == null){
        errorCallback(new DeployError(`Could not find compiled binary for contract '${key}'. Check truffle.json.`));
        return null;
      }
      // Add the contract to the depend graph
      dependsGraph.setNode(key);

      // Find references to any librarys
      // Library references are embedded in the bytecode of contracts with the format
      //  "__Lib___________________________________" , where "Lib" is your library name and the whole
      //  string is 40 characters long. This is the placeholder for the Lib's address.

      var regex = /__([^_]*)_*/g;
      var matches;
      while ( (matches = regex.exec(contract_class.unlinked_binary)) !== null ) {
        var lib = matches[1];
        if (!dependsGraph.hasEdge(key,lib)) {
          dependsGraph.setEdge(key, lib);
        }
      }
    }
    // Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
    if (!isAcyclic(dependsGraph))
    {
      console.log("ERROR: Cycles in dependency graph");
      dependsGraph.edges().forEach(function(o){
        console.log(o.v+" -- depends on --> "+o.w);
      });
      errorCallback(new DeployError(`Error  linker found cyclic dependencies. Adjust your import statements to remove cycles.`));
      return null;
    }
    return dependsGraph;
  },

  link_dependencies(config, contract_name) {
    return (dependency_addresses) => {
      var contract = config.contracts.classes[contract_name];

      //All of the library dependencies to this contract have been deployed
      //Inject the address of each lib into this contract and then deploy it.
      dependency_addresses.forEach(function(lib) {
        if (config.argv.quietDeploy == null) {
          console.log("Linking Library: " + lib.name + " to contract: " + contract_name + " at address: " + lib.address);
        }

        var bin_address = lib.address.replace("0x", "");
        var re = new RegExp("__" + lib.name + "_*", "g");
        contract.binary = contract.unlinked_binary.replace(re, bin_address);
      });

      return this.createContractAndWait(config, contract_name);
    }
  },

  deploy(config, compile=true, done_deploying) {
    var self = this;

    async.series([
      (c) => {
        self.get_account(config, c);
      },
      (c) => {
        if (compile == true) {
          this.compile_necessary(config, c);
        } else {
          c();
        }
      },
      (c) => {
        Pudding.setWeb3(config.web3);
        var dependsGraph = this.build_deploy_dependency_graph(config, c);

        if( dependsGraph == null) {
          return;
        }

        var dependsOrdering = postOrder(dependsGraph, dependsGraph.nodes());
        var deploy_promise = null;
        var contract_name; // This is in global scope so that it can be used in the .catch below

        // Iterate over the dependency grpah in post order, deploy libraries first so we can
        // capture their addresses and use them to deploy the contracts that depend on them
        for(var i = 0; i < dependsOrdering.length; i++) {
          contract_name = dependsOrdering[i];
          var contract_class = config.contracts.classes[contract_name];

          if (contract_class == null) {
            c(new DeployError(`Could not find contract '${key}' for deployment. Check truffle.json.`));
            return;
          }

          var dependencies = dependsGraph.successors(contract_name);

          // When we encounter a Library that is not dependant on other libraries, we can just
          // deploy it as normal
          if (dependencies.length == 0) {
            deploy_promise = this.createContractAndWait(config, contract_name);

            // Store the promise in the graph so we can fetch it later
            dependsGraph.setNode(contract_name, deploy_promise);
          }
          // Contracts that have dependencies need to wait until those dependencies have been deployed
          // so we can inject the address into their byte code
          else
          {
            // Collect all the promises for the libraries this contract depends on into a list
            // NOTE: since this loop is traversing in post-order, we can be assured that this list
            // will contain ALL of the dependencies of this contract
            var depends_promises = dependencies.map(dependsGraph.node, dependsGraph);

            // Wait for all the dependencies to be committed and then do the linking step
            deploy_promise = Promise.all(depends_promises).then(
              this.link_dependencies(config, contract_name)
            );

            // It's possible that this contract is a dependency of some other contract so we store
            // it in the graph just in case
            dependsGraph.setNode(contract_name,deploy_promise);
          }
        }
        ///Now wait for all of the outstanding deployments to complete
        Promise.all(dependsGraph.nodes().map(dependsGraph.node, dependsGraph))
        .then(function(deployed_contracts) {
          deployed_contracts.forEach(function(a) {
            config.contracts.classes[a.name].address = a.address;
          });
          c();
        }).catch(function(err) {
          c(new DeployError(err.message, contract_name));
        });
      },
      (c) => {
        this.write_contracts(config, "built contract files", c);
      },
      (c) => {
        this.after_deploy(config, c);
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      } else {
        done_deploying();
      }
    });
  },

  after_deploy(config, done) {
    async.eachSeries(config.app.resolved.after_deploy, function(file, iterator_callback) {
      if (config.argv.quietDeploy == null) {
        console.log(`Running post deploy script ${file}...`);
      }
      Exec.file(config, file, iterator_callback);
    }, done);
  },

  write_contracts(config, description="contracts", callback) {
    var destination = config.contracts.build_directory;
    mkdirp(destination, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "./" + path.join("./", destination.replace(config.working_dir, ""));
      if (config.argv.quietDeploy == null) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      PuddingGenerator.save(config.contracts.classes, destination, {removeExisting: true});

      callback();
    });
  }
}

module.exports = Contracts;
<commit_msg>Ensure expected contract or library was defined in .sol files. Fixes #39. Fixes #60. Fixes #61.<commit_after>var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var solc = require("solc");
var path = require("path");
var Exec = require("./exec");
var Pudding = require("ether-pudding");
var PuddingGenerator = require("ether-pudding/generator");
var ConfigurationError = require("./errors/configurationerror");
var CompileError = require("./errors/compileerror");
var DeployError = require("./errors/deployerror");
var graphlib = require("graphlib");
var Graph = require("graphlib").Graph;
var isAcyclic = require("graphlib/lib/alg").isAcyclic;
var postOrder = require("graphlib/lib/alg").postorder;

var Contracts = {
  account: null,

  get_account: function(config, callback) {
    var self = this;

    if (config.app.resolved.rpc.from != null) {
      this.account = config.app.resolved.rpc.from;
    }

    if (this.account != null) {
      return callback(null, this.account);
    }

    config.web3.eth.getAccounts(function(err, result) {
      if (err != null) return callback(err);

      self.account = result[0];
      callback(null, self.account);
    });
  },

  update_sources(config, callback) {
    var contract_names = Object.keys(config.contracts.classes);
    async.each(contract_names, function(name, done) {
      var contract = config.contracts.classes[name];
      fs.readFile(contract.file, {encoding: "utf8"}, function(err, body) {
        if (err) return done(err);

        contract.body = body;
        done();
      });
    }, callback);
  },

  compile_necessary(config, callback) {
    this.update_sources(config, () => {
      var contract_names = Object.keys(config.contracts.classes);

      var sources = {};
      var updated = {};
      var included = {};

      for (var i = 0; i < contract_names.length; i++) {
        var name = contract_names[i];
        var contract = config.contracts.classes[name];

        if (contract.source_modified_time > contract.compiled_time || config.argv.compileAll === true) {
          updated[name] = true;
        }
      }

      if (Object.keys(updated).length == 0 && config.argv.quietDeploy == null) {
        console.log("No contracts updated; skipping compilation.");
        return callback();
      }

      var dependsGraph = this.build_compile_dependency_graph(config, callback);

      if (dependsGraph == null) {
        return;
      }

      function is_updated(contract_name) {
        return updated[contract_name] === true;
      }

      function include_source_for(contract_name) {
        var contract = config.contracts.classes[contract_name];
        var source_path = contract.source.replace("./contracts/", "");

        if (sources[source_path] != null) {
          return;
        }

        var full_path = path.resolve(config.working_dir, contract.source)
        sources[source_path] = fs.readFileSync(full_path, {encoding: "utf8"});

        // For graph traversing
        included[contract_name] = true;
      }

      function walk_down(contract_name) {
        if (included[contract_name] === true) {
          return;
        }

        include_source_for(contract_name);

        var dependencies = dependsGraph.successors(contract_name);

        // console.log("At: " + contract_name);
        // console.log("   Dependencies: ", dependencies);

        if (dependencies.length > 0) {
          dependencies.forEach(walk_down);
        }
      }

      function walk_from(contract_name) {
        // if (included[contract_name] === true) {
        //   return;
        // }

        var ancestors = dependsGraph.predecessors(contract_name);
        var dependencies = dependsGraph.successors(contract_name);

        // console.log("At: " + contract_name);
        // console.log("   Ancestors: ", ancestors);
        // console.log("   Dependencies: ", dependencies);

        include_source_for(contract_name);

        if (ancestors.length > 0) {
          ancestors.forEach(walk_from);
        }

        if (dependencies.length > 0) {
          dependencies.forEach(walk_down);
        }
      }

      Object.keys(updated).forEach(walk_from);

      Object.keys(sources).sort().forEach(function(file_path) {
        if (config.argv.quietDeploy == null) {
          console.log("Compiling " + file_path + "...");
        }
      });

      var result = solc.compile({sources: sources}, 1);

      if (result.errors != null) {
        return callback(new CompileError(result.errors.join()));
      }

      // Examine the sources, and ensure the contract we expected was defined
      var filenames = Object.keys(sources);
      for (var i = 0; i < filenames.length; i++) {
        var filename = filenames[i];
        var expected_contract = path.basename(filename, ".sol");

        if (result[expected_contract] == null) {
          return callback(new CompileError("Could not find expected contract or library in '" + filename + "': contract or library '" + expected_contract + "' not found."));
        }
      }

      for (var i = 0; i < contract_names.length; i++) {
        var name = contract_names[i];
        var contract = config.contracts.classes[name];
        var compiled_contract = result.contracts[name];

        // If we didn't compile this contract this run, continue.
        if (compiled_contract == null) {
          continue;
        }

        contract.binary = compiled_contract.bytecode;
        contract.unlinked_binary = compiled_contract.bytecode;
        contract.abi = JSON.parse(compiled_contract.interface);
      }

      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        this.compile_necessary(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },

  createContractAndWait(config, contract_name) {
    var tx = {
      from: this.account,
      gas: config.app.resolved.rpc.gas || config.app.resolved.rpc.gasLimit,
      gasPrice: config.app.resolved.rpc.gasPrice, // 100 Shannon
      data: config.contracts.classes[contract_name].binary
    };

    return new Promise((accept, reject) => {
      config.web3.eth.sendTransaction(tx, (err, hash) => {
        if (err != null) {
          return reject(err);
        }

        var interval = setInterval(() => {
          config.web3.eth.getTransactionReceipt(hash, (err, receipt) => {
            if (err != null) {
              clearInterval(interval);

              return reject(err);
            }
            if (receipt != null) {
              if (config.argv.quietDeploy == null) {
                console.log("Deployed: "+contract_name+" to address: "+receipt.contractAddress);
              }

              accept({
                name: contract_name,
                address: receipt.contractAddress
              });
              clearInterval(interval);
            }
          });
        }, 500);
      })
    });
  },

  build_compile_dependency_graph(config, errorCallback) {
    if (config.argv.quietDeploy == null) {
      console.log("Checking sources...");
    }
    // Iterate through all the contracts looking for libraries and building a dependency graph
    var dependsGraph = new Graph();
    var contract_names = Object.keys(config.contracts.classes);
    for (var i = 0; i < contract_names.length; i++) {
      var name = contract_names[i]
      var contract = config.contracts.classes[name];

      if (contract == null) {
        errorCallback(new CompileError(`Could not find contract '${name}' for compiling. Check truffle.json.`));
        return null;
      }

      // Add the contract to the depend graph
      dependsGraph.setNode(name);

      // Find import statements and resolve those import paths, adding them to the graph.
      contract.body.split(/;|\n/).filter(function(line) {
        return line.indexOf("import") >= 0;
      }).forEach(function(line) {
        var regex = /import.*("|')([^"']+)("|')*/g;
        var match = regex.exec(line);

        if (match == null) return;

        var file = match[2];
        var dependency_name = path.basename(file, ".sol");

        if (!dependsGraph.hasEdge(name, dependency_name)) {
          dependsGraph.setEdge(name, dependency_name);
        }
      });
    }
    // Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
    if (!isAcyclic(dependsGraph))
    {
      console.log("ERROR: Cycles in dependency graph");
      dependsGraph.edges().forEach(function(o){
        console.log(o.v+" -- depends on --> "+o.w);
      });
      errorCallback(new CompileError(`Found cyclic dependencies. Adjust your import statements to remove cycles.`));
      return null;
    }
    return dependsGraph;
  },

  build_deploy_dependency_graph(config, errorCallback) {
    if (config.argv.quietDeploy == null) {
      console.log("Collecting dependencies...");
    }
    // Iterate through all the contracts looking for libraries and building a dependency graph
    var dependsGraph = new Graph();
    for (var i = 0; i < config.app.resolved.deploy.length; i++) {
      var key = config.app.resolved.deploy[i];
      var contract_class = config.contracts.classes[key];

      if (contract_class == null) {
        errorCallback(new DeployError(`Could not find contract '${key}' for linking. Check truffle.json.`));
        return null;
      }

      if (contract_class.binary == null){
        errorCallback(new DeployError(`Could not find compiled binary for contract '${key}'. Check truffle.json.`));
        return null;
      }
      // Add the contract to the depend graph
      dependsGraph.setNode(key);

      // Find references to any librarys
      // Library references are embedded in the bytecode of contracts with the format
      //  "__Lib___________________________________" , where "Lib" is your library name and the whole
      //  string is 40 characters long. This is the placeholder for the Lib's address.

      var regex = /__([^_]*)_*/g;
      var matches;
      while ( (matches = regex.exec(contract_class.unlinked_binary)) !== null ) {
        var lib = matches[1];
        if (!dependsGraph.hasEdge(key,lib)) {
          dependsGraph.setEdge(key, lib);
        }
      }
    }
    // Check for cycles in the graph, the dependency graph needs to be a tree otherwise there's an error
    if (!isAcyclic(dependsGraph))
    {
      console.log("ERROR: Cycles in dependency graph");
      dependsGraph.edges().forEach(function(o){
        console.log(o.v+" -- depends on --> "+o.w);
      });
      errorCallback(new DeployError(`Error  linker found cyclic dependencies. Adjust your import statements to remove cycles.`));
      return null;
    }
    return dependsGraph;
  },

  link_dependencies(config, contract_name) {
    return (dependency_addresses) => {
      var contract = config.contracts.classes[contract_name];

      //All of the library dependencies to this contract have been deployed
      //Inject the address of each lib into this contract and then deploy it.
      dependency_addresses.forEach(function(lib) {
        if (config.argv.quietDeploy == null) {
          console.log("Linking Library: " + lib.name + " to contract: " + contract_name + " at address: " + lib.address);
        }

        var bin_address = lib.address.replace("0x", "");
        var re = new RegExp("__" + lib.name + "_*", "g");
        contract.binary = contract.unlinked_binary.replace(re, bin_address);
      });

      return this.createContractAndWait(config, contract_name);
    }
  },

  deploy(config, compile=true, done_deploying) {
    var self = this;

    async.series([
      (c) => {
        self.get_account(config, c);
      },
      (c) => {
        if (compile == true) {
          this.compile_necessary(config, c);
        } else {
          c();
        }
      },
      (c) => {
        Pudding.setWeb3(config.web3);
        var dependsGraph = this.build_deploy_dependency_graph(config, c);

        if( dependsGraph == null) {
          return;
        }

        var dependsOrdering = postOrder(dependsGraph, dependsGraph.nodes());
        var deploy_promise = null;
        var contract_name; // This is in global scope so that it can be used in the .catch below

        // Iterate over the dependency grpah in post order, deploy libraries first so we can
        // capture their addresses and use them to deploy the contracts that depend on them
        for(var i = 0; i < dependsOrdering.length; i++) {
          contract_name = dependsOrdering[i];
          var contract_class = config.contracts.classes[contract_name];

          if (contract_class == null) {
            c(new DeployError(`Could not find contract '${key}' for deployment. Check truffle.json.`));
            return;
          }

          var dependencies = dependsGraph.successors(contract_name);

          // When we encounter a Library that is not dependant on other libraries, we can just
          // deploy it as normal
          if (dependencies.length == 0) {
            deploy_promise = this.createContractAndWait(config, contract_name);

            // Store the promise in the graph so we can fetch it later
            dependsGraph.setNode(contract_name, deploy_promise);
          }
          // Contracts that have dependencies need to wait until those dependencies have been deployed
          // so we can inject the address into their byte code
          else
          {
            // Collect all the promises for the libraries this contract depends on into a list
            // NOTE: since this loop is traversing in post-order, we can be assured that this list
            // will contain ALL of the dependencies of this contract
            var depends_promises = dependencies.map(dependsGraph.node, dependsGraph);

            // Wait for all the dependencies to be committed and then do the linking step
            deploy_promise = Promise.all(depends_promises).then(
              this.link_dependencies(config, contract_name)
            );

            // It's possible that this contract is a dependency of some other contract so we store
            // it in the graph just in case
            dependsGraph.setNode(contract_name,deploy_promise);
          }
        }
        ///Now wait for all of the outstanding deployments to complete
        Promise.all(dependsGraph.nodes().map(dependsGraph.node, dependsGraph))
        .then(function(deployed_contracts) {
          deployed_contracts.forEach(function(a) {
            config.contracts.classes[a.name].address = a.address;
          });
          c();
        }).catch(function(err) {
          c(new DeployError(err.message, contract_name));
        });
      },
      (c) => {
        this.write_contracts(config, "built contract files", c);
      },
      (c) => {
        this.after_deploy(config, c);
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      } else {
        done_deploying();
      }
    });
  },

  after_deploy(config, done) {
    async.eachSeries(config.app.resolved.after_deploy, function(file, iterator_callback) {
      if (config.argv.quietDeploy == null) {
        console.log(`Running post deploy script ${file}...`);
      }
      Exec.file(config, file, iterator_callback);
    }, done);
  },

  write_contracts(config, description="contracts", callback) {
    var destination = config.contracts.build_directory;
    mkdirp(destination, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "./" + path.join("./", destination.replace(config.working_dir, ""));
      if (config.argv.quietDeploy == null) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      PuddingGenerator.save(config.contracts.classes, destination, {removeExisting: true});

      callback();
    });
  }
}

module.exports = Contracts;
<|endoftext|>


<commit_before>const { IPC } = require("node-ipc");
const path = require("path");
const { spawn } = require("child_process");
const debug = require("debug");

const Develop = {
  start: async function(ipcNetwork, options = {}) {
    let chainPath;

    // The path to the dev env process depends on whether or not
    // we're running in the bundled version. If not, use chain.js
    // directly, otherwise let the bundle point at the bundled version.
    if (typeof BUNDLE_CHAIN_FILENAME !== "undefined") {
      // Remember: In the bundled version, __dirname refers to the
      // build directory where cli.bundled.js and cli.chain.js live.
      chainPath = path.join(__dirname, BUNDLE_CHAIN_FILENAME);
    } else {
      chainPath = path.join(__dirname, "./", "chain.js");
    }

    const stringifiedOptions = JSON.stringify(options);
    const optionsBuffer = Buffer.from(stringifiedOptions);
    const base64OptionsString = optionsBuffer.toString("base64");

    return spawn("node", [chainPath, ipcNetwork, base64OptionsString], {
      detached: true,
      stdio: "ignore"
    });
  },

  connect: function(options, callback) {
    const debugServer = debug("develop:ipc:server");
    const debugClient = debug("develop:ipc:client");
    const debugRPC = debug("develop:ganache");

    if (typeof options === "function") {
      callback = options;
      options = {};
    }

    options.retry = options.retry || false;
    options.log = options.log || false;
    options.network = options.network || "develop";
    var ipcNetwork = options.network;

    var ipc = new IPC();
    ipc.config.appspace = "truffle.";

    // set connectPath explicitly
    var dirname = ipc.config.socketRoot;
    var basename = `${ipc.config.appspace}${ipcNetwork}`;
    var connectPath = path.join(dirname, basename);

    ipc.config.silent = !debugClient.enabled;
    ipc.config.logger = debugClient;

    var loggers = {};

    if (debugServer.enabled) {
      loggers.ipc = debugServer;
    }

    if (options.log) {
      debugRPC.enabled = true;

      loggers.ganache = function() {
        // HACK-y: replace `{}` that is getting logged instead of ""
        var args = Array.prototype.slice.call(arguments);
        if (
          args.length === 1 &&
          typeof args[0] === "object" &&
          Object.keys(args[0]).length === 0
        ) {
          args[0] = "";
        }

        debugRPC.apply(undefined, args);
      };
    }

    if (!options.retry) {
      ipc.config.maxRetries = 0;
    }

    var disconnect = function() {
      ipc.disconnect(ipcNetwork);
    };

    ipc.connectTo(ipcNetwork, connectPath, function() {
      ipc.of[ipcNetwork].on("destroy", function() {
        callback(new Error("IPC connection destroyed"));
      });

      ipc.of[ipcNetwork].on("truffle.ready", function() {
        callback(null, disconnect);
      });

      Object.keys(loggers).forEach(function(key) {
        var log = loggers[key];
        if (log) {
          var message = `truffle.${key}.log`;
          ipc.of[ipcNetwork].on(message, log);
        }
      });
    });
  },

  connectOrStart: function(options, ganacheOptions, callback) {
    const self = this;

    options.retry = false;

    const ipcNetwork = options.network || "develop";

    let connectedAlready = false;

    this.connect(
      options,
      async function(error, disconnect) {
        if (error) {
          await self.start(ipcNetwork, ganacheOptions);

          options.retry = true;
          self.connect(
            options,
            function(error, disconnect) {
              if (connectedAlready) return;

              connectedAlready = true;
              callback(true, disconnect);
            }
          );
        } else {
          connectedAlready = true;
          callback(false, disconnect);
        }
      }
    );
  }
};

module.exports = Develop;
<commit_msg>add some validation to truffle-environment/develop.js for the genesis-time option set in truffle-config.js.<commit_after>const { IPC } = require("node-ipc");
const path = require("path");
const { spawn } = require("child_process");
const debug = require("debug");

const Develop = {
  start: async function(ipcNetwork, options = {}) {
    let chainPath;

    // The path to the dev env process depends on whether or not
    // we're running in the bundled version. If not, use chain.js
    // directly, otherwise let the bundle point at the bundled version.
    if (typeof BUNDLE_CHAIN_FILENAME !== "undefined") {
      // Remember: In the bundled version, __dirname refers to the
      // build directory where cli.bundled.js and cli.chain.js live.
      chainPath = path.join(__dirname, BUNDLE_CHAIN_FILENAME);
    } else {
      chainPath = path.join(__dirname, "./", "chain.js");
    }

    const logger = options.logger || console;
    logger.log(`In Env - Genesis Block time: ${options.time}`);
    //check that genesis-time config option passed through the truffle-config.js file is a valid time.
    const checkDate = Date.parse(options.time);
    logger.log(`In Env - Date Object: ${checkDate}`);
    if (isNaN(checkDate)) {
      throw new Error(
        "Fatal: genesis-time passed through truffle-config is invalid."
      );
    }

    const stringifiedOptions = JSON.stringify(options);
    const optionsBuffer = Buffer.from(stringifiedOptions);
    const base64OptionsString = optionsBuffer.toString("base64");

    return spawn("node", [chainPath, ipcNetwork, base64OptionsString], {
      detached: true,
      stdio: "ignore"
    });
  },

  connect: function(options, callback) {
    const debugServer = debug("develop:ipc:server");
    const debugClient = debug("develop:ipc:client");
    const debugRPC = debug("develop:ganache");

    if (typeof options === "function") {
      callback = options;
      options = {};
    }

    options.retry = options.retry || false;
    options.log = options.log || false;
    options.network = options.network || "develop";
    var ipcNetwork = options.network;

    var ipc = new IPC();
    ipc.config.appspace = "truffle.";

    // set connectPath explicitly
    var dirname = ipc.config.socketRoot;
    var basename = `${ipc.config.appspace}${ipcNetwork}`;
    var connectPath = path.join(dirname, basename);

    ipc.config.silent = !debugClient.enabled;
    ipc.config.logger = debugClient;

    var loggers = {};

    if (debugServer.enabled) {
      loggers.ipc = debugServer;
    }

    if (options.log) {
      debugRPC.enabled = true;

      loggers.ganache = function() {
        // HACK-y: replace `{}` that is getting logged instead of ""
        var args = Array.prototype.slice.call(arguments);
        if (
          args.length === 1 &&
          typeof args[0] === "object" &&
          Object.keys(args[0]).length === 0
        ) {
          args[0] = "";
        }

        debugRPC.apply(undefined, args);
      };
    }

    if (!options.retry) {
      ipc.config.maxRetries = 0;
    }

    var disconnect = function() {
      ipc.disconnect(ipcNetwork);
    };

    ipc.connectTo(ipcNetwork, connectPath, function() {
      ipc.of[ipcNetwork].on("destroy", function() {
        callback(new Error("IPC connection destroyed"));
      });

      ipc.of[ipcNetwork].on("truffle.ready", function() {
        callback(null, disconnect);
      });

      Object.keys(loggers).forEach(function(key) {
        var log = loggers[key];
        if (log) {
          var message = `truffle.${key}.log`;
          ipc.of[ipcNetwork].on(message, log);
        }
      });
    });
  },

  connectOrStart: function(options, ganacheOptions, callback) {
    const self = this;

    options.retry = false;

    const ipcNetwork = options.network || "develop";

    let connectedAlready = false;

    this.connect(options, async function(error, disconnect) {
      if (error) {
        await self.start(ipcNetwork, ganacheOptions);

        options.retry = true;
        self.connect(options, function(error, disconnect) {
          if (connectedAlready) return;

          connectedAlready = true;
          callback(true, disconnect);
        });
      } else {
        connectedAlready = true;
        callback(false, disconnect);
      }
    });
  }
};

module.exports = Develop;
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

Sidebar.Geometry.BufferGeometry = function ( signals ) {

	var container = new UI.Panel();

	// vertices

	var verticesRow = new UI.Panel();
	var vertices = new UI.Text().setFontSize( '12px' );

	verticesRow.add( new UI.Text( 'Vertices' ).setWidth( '90px' ) );
	verticesRow.add( vertices );

	container.add( verticesRow );

	// faces

	var facesRow = new UI.Panel();
	var faces = new UI.Text().setFontSize( '12px' );

	facesRow.add( new UI.Text( 'Faces' ).setWidth( '90px' ) );
	facesRow.add( faces );

	container.add( facesRow );

	//

	var update = function ( object ) {

		if ( object === null ) return;

		var geometry = object.geometry;

		if ( geometry instanceof THREE.BufferGeometry ) {

			container.setDisplay( 'block' );

			vertices.setValue( ( geometry.attributes.position.array.length / 3 ).format() );

			if ( geometry.attributes.index !== undefined ) {

				faces.setValue( ( geometry.attributes.index.array.length / 3 ).format() );

			} else {

				faces.setValue( ( geometry.attributes.position.array.length / 9 ).format() );

			}

		} else {

			container.setDisplay( 'none' );

		}

	};

	signals.objectSelected.add( update );
	signals.geometryChanged.add( update );

	return container;

}
<commit_msg>Editor: More useful BufferGeometry panel.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

Sidebar.Geometry.BufferGeometry = function ( signals ) {

	var container = new UI.Panel();

	function update( object ) {

		if ( object === null ) return;

		var geometry = object.geometry;

		if ( geometry instanceof THREE.BufferGeometry ) {

			container.clear();
			container.setDisplay( 'block' );

			var attributes = geometry.attributes;

			for ( var name in attributes ) {

				var panel = new UI.Panel();
				panel.add( new UI.Text( name ).setWidth( '90px' ) );
				panel.add( new UI.Text( ( attributes[ name ].count ).format() ).setFontSize( '12px' ) );
				container.add( panel );

			}

		} else {

			container.setDisplay( 'none' );

		}

	};

	signals.objectSelected.add( update );
	signals.geometryChanged.add( update );

	return container;

}
<|endoftext|>


<commit_before>import { max as d3Max } from 'd3-array';
import caseInsensitiveSort from './caseInsensitiveSort';

/*
 * Computes the bin and value domains from numeric or categorical bins
 */
export default function computeDomainsFromBins({ binsByIndex, binType, valueKey, binValues }) {
  let binDomain;
  let valueDomain;

  Object.values(binsByIndex).forEach(bins => {
    const currValueMax = d3Max(bins, d => d[valueKey]);
    if (valueDomain) {
      valueDomain[1] = Math.max(currValueMax, valueDomain[1]);
    } else {
      valueDomain = [0, currValueMax];
    }

    if (binType === 'numeric') {
      if (binDomain) {
        binDomain[0] = Math.min(bins[0].bin0, binDomain[0]);
        binDomain[1] = Math.max(bins[bins.length - 1].bin1, binDomain[1]);
      } else {
        binDomain = [bins[0].bin0, bins[bins.length - 1].bin1];
      }
    } else {
      if (!binDomain) binDomain = {}; // use lookup to avoid lots of Array scans
      bins.forEach(bin => {
        binDomain[bin.bin] = true;
      });
    }
  });

  if (Array.isArray(binValues)) {
    let providedBinsCoverDomain = true;
    binValues.forEach(binValue => {
      if (!(binValue in binDomain)) {
        providedBinsCoverDomain = false
      }
    })

    if (providedBinsCoverDomain) {
      binDomain = binValues
    }
  }

  if (!Array.isArray(binDomain)) {
    if (binDomain === undefined) {
      binDomain = Object.keys(binDomain).sort(caseInsensitiveSort);
    }
  }

  return { binDomain, valueDomain };
}
<commit_msg>Test fix; undefined fix needs to be moved into parent conditional<commit_after>import { max as d3Max } from 'd3-array';
import caseInsensitiveSort from './caseInsensitiveSort';

/*
 * Computes the bin and value domains from numeric or categorical bins
 */
export default function computeDomainsFromBins({ binsByIndex, binType, valueKey, binValues }) {
  let binDomain;
  let valueDomain;

  Object.values(binsByIndex).forEach(bins => {
    const currValueMax = d3Max(bins, d => d[valueKey]);
    if (valueDomain) {
      valueDomain[1] = Math.max(currValueMax, valueDomain[1]);
    } else {
      valueDomain = [0, currValueMax];
    }

    if (binType === 'numeric') {
      if (binDomain) {
        binDomain[0] = Math.min(bins[0].bin0, binDomain[0]);
        binDomain[1] = Math.max(bins[bins.length - 1].bin1, binDomain[1]);
      } else {
        binDomain = [bins[0].bin0, bins[bins.length - 1].bin1];
      }
    } else {
      if (!binDomain) binDomain = {}; // use lookup to avoid lots of Array scans
      bins.forEach(bin => {
        binDomain[bin.bin] = true;
      });
    }
  });

  if (Array.isArray(binValues)) {
    let providedBinsCoverDomain = true;
    binValues.forEach(binValue => {
      if (!(binValue in binDomain)) {
        providedBinsCoverDomain = false
      }
    })

    if (providedBinsCoverDomain) {
      binDomain = binValues
    }
  }

  if (!Array.isArray(binDomain)) {
    binDomain = Object.keys(binDomain).sort(caseInsensitiveSort);
  }

  return { binDomain, valueDomain };
}
<|endoftext|>


<commit_before>// @flow
import querystring from 'qs';
import uuid from 'uuid';
import { ZonedDateTime, ZoneId } from 'js-joda';

import { ACTIVITY_PLUGIN_NAME } from '../config';

// Ensure timezone info is available
require('js-joda-timezone');

type UpdateId = string => (Object, ?boolean) => string;
/*
 * This is a 'get or set' function for the `cookieName` passed in.
 */
export const updateId: UpdateId = cookieName => (
	request: Object,
	doRefresh: ?boolean
) => {
	let cookieVal: string =
		request.state[cookieName] || // cookie in original request
		request.plugins[ACTIVITY_PLUGIN_NAME][cookieName]; // cookie added to outgoing response

	if (!cookieVal || doRefresh) {
		// Generate a new id value and store in request. Cookie will be
		// set in the plugin's onResponse handler
		return newId(cookieName)(request);
	}
	return parseIdCookie(cookieVal.toString() || '').toString(); // toString used to satisfy Flow
};

/*
 * This function creates a new uuid and stores it in the request using a
 * `cookieName` key. The actual cookie will be set in the plugin's `onResponse`
 * handler in order to share the cookie across browser tabs.
 */
export const newId = (cookieName: string) => (request: HapiRequest): string => {
	const id: string = uuid.v4();
	request.plugins[ACTIVITY_PLUGIN_NAME][cookieName] = makeIdCookie(id);
	return id;
};

// chapstick cookies generally wrap cookie values with quotes, which is broken
// and wrong, but when in Rome...
export const makeIdCookie = (id: string) => `"id=${id}"`;
export const parseIdCookie = (
	cookieVal: ?string | ?Array<string>,
	doParseInt?: boolean
) => {
	const cleanCookie =
		cookieVal && cookieVal instanceof Array ? cookieVal[0] : cookieVal;
	const parsed: { id: string } = querystring.parse(
		(cleanCookie || '').toString().replace(/^"|"$/g, '') // strip nasty leading/ending quotes
	) || { id: '' };
	parsed.id = parsed.id || '';
	if (doParseInt) {
		return parseInt(parsed.id, 10) || 0;
	}
	return parsed.id;
};

// `toString()` adds the timezone in brackets, which we want to remove
// e.g.: 2019-01-07T10:27:02.791-05:00[America/New_York]
export const getISOStringWithUTCOffset = (zonedDateTime: ZonedDateTime) =>
	zonedDateTime.toString().replace(/\[.*\]$/, '');

// Get current time as an ISO Date Time String with UTC offset
export const getISOStringNow = (tz: string = 'America/New_York') =>
	getISOStringWithUTCOffset(ZonedDateTime.now(ZoneId.of(tz)));
<commit_msg>replace regex string replacement w/ formatter for offset formatting<commit_after>// @flow
import querystring from 'qs';
import uuid from 'uuid';
import { DateTimeFormatter, ZonedDateTime, ZoneId } from 'js-joda';

import { ACTIVITY_PLUGIN_NAME } from '../config';

// Ensure timezone info is available
require('js-joda-timezone');

type UpdateId = string => (Object, ?boolean) => string;
/*
 * This is a 'get or set' function for the `cookieName` passed in.
 */
export const updateId: UpdateId = cookieName => (
	request: Object,
	doRefresh: ?boolean
) => {
	let cookieVal: string =
		request.state[cookieName] || // cookie in original request
		request.plugins[ACTIVITY_PLUGIN_NAME][cookieName]; // cookie added to outgoing response

	if (!cookieVal || doRefresh) {
		// Generate a new id value and store in request. Cookie will be
		// set in the plugin's onResponse handler
		return newId(cookieName)(request);
	}
	return parseIdCookie(cookieVal.toString() || '').toString(); // toString used to satisfy Flow
};

/*
 * This function creates a new uuid and stores it in the request using a
 * `cookieName` key. The actual cookie will be set in the plugin's `onResponse`
 * handler in order to share the cookie across browser tabs.
 */
export const newId = (cookieName: string) => (request: HapiRequest): string => {
	const id: string = uuid.v4();
	request.plugins[ACTIVITY_PLUGIN_NAME][cookieName] = makeIdCookie(id);
	return id;
};

// chapstick cookies generally wrap cookie values with quotes, which is broken
// and wrong, but when in Rome...
export const makeIdCookie = (id: string) => `"id=${id}"`;
export const parseIdCookie = (
	cookieVal: ?string | ?Array<string>,
	doParseInt?: boolean
) => {
	const cleanCookie =
		cookieVal && cookieVal instanceof Array ? cookieVal[0] : cookieVal;
	const parsed: { id: string } = querystring.parse(
		(cleanCookie || '').toString().replace(/^"|"$/g, '') // strip nasty leading/ending quotes
	) || { id: '' };
	parsed.id = parsed.id || '';
	if (doParseInt) {
		return parseInt(parsed.id, 10) || 0;
	}
	return parsed.id;
};

// `toString()` adds the timezone in brackets, which we want to remove
// e.g.: 2019-01-07T10:27:02.791-05:00[America/New_York]
export const getISOStringWithUTCOffset = (zonedDateTime: ZonedDateTime) =>
	DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime);

// Get current time as an ISO Date Time String with UTC offset
export const getISOStringNow = (tz: string = 'America/New_York') =>
	getISOStringWithUTCOffset(ZonedDateTime.now(ZoneId.of(tz)));
<|endoftext|>


<commit_before>
function passRequestParameters(flowHelper, usecase) {
	var names = cocoon.request.getParameterNames();
	while (names.hasMoreElements()) {
	    var name = names.nextElement();
		if (!name.equals("lenya.usecase")
			&& !name.equals("lenya.continuation")
			&& !name.equals("submit")) {

			var value = flowHelper.getRequest(cocoon).get(name);

			var string = new Packages.java.lang.String();
			if (string.getClass().isInstance(value)) {
				usecase.setParameter(name, value);
			}
			else {
				usecase.setPart(name, value);
			}

		}
	}
}

function executeUsecase() {
	var usecaseName = cocoon.request.getParameter("lenya.usecase");
	var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
	var usecase = usecaseResolver.resolve(usecaseName);

	var flowHelper = new Packages.org.apache.lenya.cms.cocoon.flow.FlowHelper();
	var situation = flowHelper.getSituation(cocoon);
	var envelope = flowHelper.getPageEnvelope(cocoon);
	var document = envelope.getDocument();

	passRequestParameters(flowHelper, usecase);

	var ready = false;
	var success = false;

	var usecaseView = new Packages.java.lang.String(usecaseName).replace('.', '/');

	while (!ready) {

		cocoon.sendPageAndWait("view/" + usecaseView, {
		    "usecase" : usecase
		});

		passRequestParameters(flowHelper, usecase);
		usecase.advance();

		if (cocoon.request.getParameter("submit")) {
			usecase.checkExecutionConditions();
			if (usecase.getErrorMessages().isEmpty()) {
				usecase.execute();
				if (usecase.getErrorMessages().isEmpty()) {
					usecase.checkPostconditions();
					if (usecase.getErrorMessages().isEmpty()) {
						ready = true;
						success = true;
					}
				}
			}
		}
		else if (cocoon.request.getParameter("cancel")) {
			ready = true;
		}

	}

	var url = envelope.getContext() + usecase.getTargetURL(success);
	cocoon.redirectTo(url);

}
<commit_msg>Added comments and license to usecases.js<commit_after>/*
 * Copyright  1999-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/* Helper method to add all request parameters to a usecase */
function passRequestParameters(flowHelper, usecase) {
	var names = cocoon.request.getParameterNames();
	while (names.hasMoreElements()) {
	    var name = names.nextElement();
		if (!name.equals("lenya.usecase")
			&& !name.equals("lenya.continuation")
			&& !name.equals("submit")) {

			var value = flowHelper.getRequest(cocoon).get(name);

			var string = new Packages.java.lang.String();
			if (string.getClass().isInstance(value)) {
				usecase.setParameter(name, value);
			}
			else {
				usecase.setPart(name, value);
			}

		}
	}
}

function executeUsecase() {
	var usecaseName = cocoon.request.getParameter("lenya.usecase");
	var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
	var usecase = usecaseResolver.resolve(usecaseName);

	var flowHelper = new Packages.org.apache.lenya.cms.cocoon.flow.FlowHelper();
	var situation = flowHelper.getSituation(cocoon);
	var envelope = flowHelper.getPageEnvelope(cocoon);
	var document = envelope.getDocument();

	passRequestParameters(flowHelper, usecase);

	var ready = false;
	var success = false;

	var usecaseView = new Packages.java.lang.String(usecaseName).replace('.', '/');

	while (!ready) {

		cocoon.sendPageAndWait("view/" + usecaseView, {
		    "usecase" : usecase
		});

		passRequestParameters(flowHelper, usecase);
		usecase.advance();

		if (cocoon.request.getParameter("submit")) {
			usecase.checkExecutionConditions();
			if (usecase.getErrorMessages().isEmpty()) {
				usecase.execute();
				if (usecase.getErrorMessages().isEmpty()) {
					usecase.checkPostconditions();
					if (usecase.getErrorMessages().isEmpty()) {
						ready = true;
						success = true;
					}
				}
			}
		}
		else if (cocoon.request.getParameter("cancel")) {
			ready = true;
		}

	}

	var url = envelope.getContext() + usecase.getTargetURL(success);
	cocoon.redirectTo(url);

}
<|endoftext|>


<commit_before>function toGeoJSON(data,cb){
    var outPut = { "type": "FeatureCollection",
  "features": []};
    var fl = data.features.length;
    var i = 0;
    while(fl>i){
     var ft = data.features[i];
/* as only ESRI based products care if all the features are the same type of geometry, check for geometry type at a feature level*/
     var outFT = {
            "type": "Feature",
            "properties":ft.attributes
        };
        if(ft.geometry.x){
//check if it's a point
          outFT.geometry=point(ft.geometry);
        }else if(ft.geometry.points){
//check if it is a multipoint
            outFT.geometry=points(ft.geometry);
            }else if(ft.geometry.paths){
//check if a line (or "ARC" in ESRI terms)
         outFT.geometry=line(ft.geometry);
        }else if(ft.geometry.rings){
//check if a poly.
           outFT.geometry=poly(ft.geometry);
        }

     outPut.features.push(outFT);
     i++;
    }
function point(geometry){
//this one is easy
    return {"type": "Point","coordinates": [geometry.x,geometry.y]};
}
function points(geometry){
//checks if the multipoint only has one point, if so exports as point instead
    if(geometry.points.length===1){
        return {"type": "Point","coordinates": geometry.points[0]};
    }else{
        return { "type": "MultiPoint","coordinates":geometry.points};
    }
}
function line(geometry){
//checks if their are multiple paths or just one
    if(geometry.paths.length===1){
        return {"type": "LineString","coordinates": geometry.paths[0]};
    }else{
        return { "type": "MultiLineString","coordinates":geometry.paths};
    }
}
function poly(geometry){
//first we check for some easy cases, like if their is only one ring
    if(geometry.rings.length===1){
        return {"type": "Polygon","coordinates": geometry.rings};
    }else{
/*if it isn't that easy then we have to start checking ring direction, basically the ring goes clockwise its part of the polygon, if it goes counterclockwise it is a hole in the polygon, but geojson does it by haveing an array with the first element be the polygons and the next elements being holes in it*/
        var ccc= dP(geometry.rings);
        var d = ccc[0];
        var dd = ccc[1];
        var r=[];
        if(dd.length===0){
/*if their are no holes we don't need to worry about this, but do need to stuck each ring inside its own array*/
            var l2 = d.length;
            var i3 = 0;
            while(l2>i3){
             r.push([d[i3]]);
            }
            return { "type": "MultiPolygon","coordinates":r};
        }else if(d.length===1){
/*if their is only one clockwise ring then we know all holes are in that poly*/
            dd.unshift(d[0]);
            return {"type": "Polygon","coordinates": dd};

        }else{
/*if their are multiple rings and holes we have no way of knowing which belong to which without looking at it specially, so just dump the coordinates and add  a hole field, this may cause errors*/
            return { "type": "MultiPolygon","coordinates":d, "holes":dd};
        }
    }
}
function dP(a){
//returns an array of 2 arrays, the first being all the clockwise ones, the second counter clockwise
    var d = [];
        var dd =[];
        var l = a.length;
        var ii = 0;
        while(l>ii){
            if(c(a[ii])){
                d.push(a[ii]);
            }else{
             dd.push(a[ii]);
            }
         ii++;
        }
    return [d,dd];
}
function c(a){
//return true if clockwise
 var l = a.length-1;
 var i = 0;
 var o=0;

 while(l>i){
 o+=(a[i][0]*a[i+1][1]-a[i+1][0]*a[i][1]);

     i++;
 }
    return o<=0;
}
if(cb){
 cb(outPut)
}else{
return outPut;
}
}
if (typeof module !== undefined) module.exports =toGeoJSON;<commit_msg>cleaned up polygon code<commit_after>function toGeoJSON(data,cb){
    var outPut = { "type": "FeatureCollection",
  "features": []};
    var fl = data.features.length;
    var i = 0;
    while(fl>i){
     var ft = data.features[i];
/* as only ESRI based products care if all the features are the same type of geometry, check for geometry type at a feature level*/
     var outFT = {
            "type": "Feature",
            "properties":ft.attributes
        };
        if(ft.geometry.x){
//check if it's a point
          outFT.geometry=point(ft.geometry);
        }else if(ft.geometry.points){
//check if it is a multipoint
            outFT.geometry=points(ft.geometry);
            }else if(ft.geometry.paths){
//check if a line (or "ARC" in ESRI terms)
         outFT.geometry=line(ft.geometry);
        }else if(ft.geometry.rings){
//check if a poly.
           outFT.geometry=poly(ft.geometry);
        }

     outPut.features.push(outFT);
     i++;
    }
function point(geometry){
//this one is easy
    return {"type": "Point","coordinates": [geometry.x,geometry.y]};
}
function points(geometry){
//checks if the multipoint only has one point, if so exports as point instead
    if(geometry.points.length===1){
        return {"type": "Point","coordinates": geometry.points[0]};
    }else{
        return { "type": "MultiPoint","coordinates":geometry.points};
    }
}
function line(geometry){
//checks if their are multiple paths or just one
    if(geometry.paths.length===1){
        return {"type": "LineString","coordinates": geometry.paths[0]};
    }else{
        return { "type": "MultiLineString","coordinates":geometry.paths};
    }
}
function poly(geometry){
//first we check for some easy cases, like if their is only one ring
    if(geometry.rings.length===1){
        return {"type": "Polygon","coordinates": geometry.rings};
    }else{
/*if it isn't that easy then we have to start checking ring direction, basically the ring goes clockwise its part of the polygon, if it goes counterclockwise it is a hole in the polygon, but geojson does it by haveing an array with the first element be the polygons and the next elements being holes in it*/
        return decodePolygon(geometry.rings);
    }
}
function decodePolygon(a){
//returns the feature
    var coords = [],type;
    var len = a.length;
    var i = 0;
    var len2 = coords.length-1;
    while(len>i){
        if(c(a[i])){
            coords.push([a[i]]);
            len2++;
        }else{
         coords[len2].push(a[i]);
        }
     i++;
    }
    if(coords.length===1){
        type="Polygon";
    }else{
        type="MultiPolygon";
    }
    return {"type":type,"coordinates":coords};
}
function c(a){
//return true if clockwise
 var l = a.length-1;
 var i = 0;
 var o=0;

 while(l>i){
    o+=(a[i+1][0]-a[i][0])*(a[i+1][1]-a[i][0]);
    i++;
 }
    return o>=0;
}
if(cb){
 cb(outPut);
}else{
return outPut;
}
}
if (typeof module !== undefined) module.exports =toGeoJSON;<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';

import currency from '../../localization/currency';

import { ROW_BLUE, dataTableStyles, SUSSOL_ORANGE } from '../../globalStyles';

import Row from './Row';
import Cell from './Cell';

import CheckableCell from './CheckableCell';
import TouchableCell from './TouchableCell';
import DropDownCell from '../DropDownCell';
import TextInputCell from './TextInputCell';
import { ExpiryDateInput } from '../ExpiryDateInput';
import {
  CheckedIcon,
  UncheckedIcon,
  DisabledCheckedIcon,
  DisabledUncheckedIcon,
  ChevronRightIcon,
  HistoryIcon,
  PencilIcon,
  HazardIcon,
  BookIcon,
} from '../icons';

import { COLUMN_TYPES, COLUMN_KEYS } from '../../pages/dataTableUtilities';
import { generalStrings, tableStrings } from '../../localization';
import { formatStatus, formatDate } from '../../utilities';
import { formatType } from '../../utilities/formatStatus';
import { useDebounce } from '../../hooks';

/**
 * Wrapper component for a mSupply DataTable page row.
 * Wraps Row component with a contained RenderCells, which
 * will generate the appropriate cell for a given columnKey.
 * Doesn't need to be used, but is a convenience component.
 *
 * @param {Object} rowData     Data object for a row i.e. ItemBatch object
 * @param {Object} rowState    State object for a row, see: Row.js
 * @param {Object} style       Style object the be passed to inner Row
 * @param {String} rowKey      Unique key for a row
 * @param {Array}  columns     Array of column objects, see: columns.js
 * @param {Bool}   isFinalised Boolean indicating if the DataTable page is finalised.
 * @param {Func}   rowIndex    index of this row.
 * @param {Func}   onPress     On press callback for the row itself.
 * @param {Func}   getCallback Function to return a cells callback
 *                             (colKey, propName) => callback
 */
const DataTableRow = React.memo(
  ({
    rowData,
    rowState,
    rowKey,
    columns,
    isFinalised,
    getCallback,
    onPress,
    rowIndex,
    getCellError,
    isValidated,
  }) => {
    const {
      cellText,
      cellContainer,
      warningCellContainer,
      errorCellContainer,
      touchableCellContainer,
      editableCellText,
      editableCellTextView,
      editableCellUnfocused,
      selectedRow: selectedRowStyle,
      alternateRow: alternateRowStyle,
      row: basicRowStyle,
      iconCell,
    } = dataTableStyles;

    const { isSelected = false } = rowState || {};

    const [hasFocus, setFocused] = React.useState(false);
    const focus = useDebounce(shouldFocus => setFocused(shouldFocus), 50, false);

    const rowStyle = useMemo(
      () => ({
        ...(rowIndex % 2 === 0 ? alternateRowStyle : basicRowStyle),
        ...(isSelected && selectedRowStyle),
        ...(!isValidated && { borderColor: SUSSOL_ORANGE, borderWidth: 1, borderRadius: 5 }),
        ...(hasFocus && { backgroundColor: ROW_BLUE }),
      }),
      [rowIndex, isSelected, isValidated, hasFocus]
    );

    // Callback for rendering a row of cells.
    const renderCells = useCallback(
      () =>
        // Map each column to an appropriate cell for a given row.
        columns.map(({ key: columnKey, type, width, alignText, icon }, index) => {
          // Indicator if the right hand border should be removed from styles for this cell.
          const isLastCell = index === columns.length - 1;

          // This cell is disabled if:
          // - the page is finalised.
          // - the row has been explicitly set as disabled.
          const rowIsDisabled = rowState?.isDisabled ?? false;
          const isDisabled = isFinalised || rowIsDisabled;

          // Alignment of this particular column. Default to left hand ide.
          const cellAlignment = alignText || 'left';

          switch (type) {
            case COLUMN_TYPES.EDITABLE_STRING:
            case COLUMN_TYPES.EDITABLE_NUMERIC: {
              const { isVaccine, hasBeenCounted } = rowData;

              // Placeholder values which are determined on a per row basis. For example, display
              // "Not counted" as a placeholder on a stocktake row. Placeholders are used under
              // the condition [placeholder && !value] which can be a source of bugs with JS
              // falsey values. I.e. if a value is 0, the placeholder will display rather than
              // the value.
              const extraPlaceholders = {
                [COLUMN_KEYS.COUNTED_TOTAL_QUANTITY]: hasBeenCounted
                  ? ''
                  : tableStrings.not_counted,
              };

              // Extra disabled conditions for columns which can be determined per row. For example,
              // only allow the doses column for vaccine items.
              const disabledCondition = {
                [COLUMN_KEYS.DOSES]: !isVaccine,
              };

              // Extra text which can be displayed for a column on a per row basis when the
              // cell is disabled. For example display N/A on a row for the doses column rather
              // than the underlying value "0".
              const disabledText = {
                [COLUMN_KEYS.DOSES]: isVaccine ? '' : generalStrings.not_available,
              };

              const value = disabledText[columnKey] ? disabledText[columnKey] : rowData[columnKey];
              const placeholder = extraPlaceholders[columnKey] ?? '';

              const inputIsDisabled = isDisabled || !!disabledCondition[columnKey];

              const cellErrors = getCellError?.(rowData, columnKey);
              const alternateCellStyleLookup = {
                warning: warningCellContainer,
                error: errorCellContainer,
              };
              const cellContainerStyle = alternateCellStyleLookup[cellErrors] ?? cellContainer;

              return (
                <TextInputCell
                  onFocus={() => focus(true)}
                  onBlur={() => focus(false)}
                  key={columnKey}
                  value={value}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onChangeText={getCallback(columnKey)}
                  isDisabled={inputIsDisabled}
                  width={width}
                  viewStyle={cellContainerStyle[cellAlignment]}
                  textViewStyle={editableCellTextView}
                  isLastCell={isLastCell}
                  keyboardType={type === COLUMN_TYPES.EDITABLE_NUMERIC ? 'numeric' : 'default'}
                  textInputStyle={cellText[cellAlignment]}
                  textStyle={editableCellUnfocused[cellAlignment]}
                  cellTextStyle={editableCellText}
                  rowIndex={rowIndex}
                  placeholder={placeholder}
                />
              );
            }
            case COLUMN_TYPES.EDITABLE_EXPIRY_DATE:
              return (
                <ExpiryDateInput
                  onFocus={() => focus(true)}
                  onBlur={() => focus(false)}
                  key={columnKey}
                  value={rowData[columnKey]}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onEndEditing={getCallback(columnKey)}
                  isDisabled={isDisabled}
                  width={width}
                  isLastCell={isLastCell}
                  rowIndex={rowIndex}
                />
              );

            case COLUMN_TYPES.CHECKABLE:
              return (
                <CheckableCell
                  key={columnKey}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  isChecked={rowState && rowState.isSelected}
                  isDisabled={isDisabled}
                  CheckedComponent={CheckedIcon}
                  UncheckedComponent={UncheckedIcon}
                  DisabledCheckedComponent={DisabledCheckedIcon}
                  DisabledUncheckedComponent={DisabledUncheckedIcon}
                  onCheck={getCallback(columnKey, 'onCheck')}
                  onUncheck={getCallback(columnKey, 'onUncheck')}
                  containerStyle={touchableCellContainer}
                  width={width}
                  isLastCell={isLastCell}
                />
              );

            case COLUMN_TYPES.CURRENCY: {
              const value = currency(rowData[columnKey]).format(false);

              return (
                <Cell
                  key={columnKey}
                  value={value}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.STRING: {
              const value = rowData[columnKey];
              let displayValue = columnKey === 'status' ? formatStatus(value) : value;
              displayValue = columnKey === 'type' ? formatType(displayValue) : displayValue;

              return (
                <Cell
                  key={columnKey}
                  value={displayValue}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.NUMERIC: {
              // Special condition for stocktake difference cells.
              // Use the placeholder 'Not counted' when a stocktake item or batch
              // has not been counted yet.
              const value =
                columnKey === COLUMN_KEYS.DIFFERENCE && !rowData.hasBeenCounted
                  ? generalStrings.not_available
                  : Math.round(rowData[columnKey]);

              const cellErrors = getCellError?.(rowData, columnKey);
              const alternateCellStyleLookup = {
                warning: warningCellContainer,
                error: errorCellContainer,
              };
              const cellContainerStyle = alternateCellStyleLookup[cellErrors] ?? cellContainer;

              return (
                <Cell
                  key={columnKey}
                  value={value}
                  width={width}
                  viewStyle={cellContainerStyle[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.DATE:
              return (
                <Cell
                  key={columnKey}
                  value={formatDate(rowData[columnKey], 'll') ?? generalStrings.not_available}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );

            case COLUMN_TYPES.ICON: {
              const { hasBreached, isVaccine } = rowData ?? {};

              const icons = {
                chevron_right: ChevronRightIcon,
                history: () => <HistoryIcon color={SUSSOL_ORANGE} />,
                pencil: () => <PencilIcon color={SUSSOL_ORANGE} />,
                breach: () => (hasBreached ? <HazardIcon color={SUSSOL_ORANGE} /> : null),
                book: () => <BookIcon color={SUSSOL_ORANGE} />,
              };

              const disabledConditions = {
                [COLUMN_KEYS.HAS_BREACHED]: !(hasBreached && isVaccine),
              };

              const isEditReadOnlyRecord =
                (columnKey === COLUMN_KEYS.PATIENT_EDIT ||
                  columnKey === COLUMN_KEYS.PRESCRIBER_EDIT) &&
                !rowData.isEditable;
              const iconComponent = isEditReadOnlyRecord ? icons.book : icons[icon];

              const isDisabledIcon =
                (isDisabled || disabledConditions[columnKey]) && columnKey !== COLUMN_KEYS.BATCH;

              return (
                <TouchableCell
                  key={columnKey}
                  renderChildren={iconComponent}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onPress={getCallback(columnKey)}
                  width={width}
                  isLastCell={isLastCell}
                  isDisabled={isDisabledIcon}
                  containerStyle={iconCell}
                />
              );
            }

            case COLUMN_TYPES.DROP_DOWN: {
              const { isVaccine = false, hasVariance = false } = rowData ?? {};

              const disabledConditions = {
                [COLUMN_KEYS.CURRENT_VVM_STATUS]: !isVaccine,
                [COLUMN_KEYS.REASON_TITLE]: !hasVariance,
              };

              const extraDisabledCondition = disabledConditions[columnKey];

              return (
                <DropDownCell
                  key={columnKey}
                  isDisabled={isDisabled || extraDisabledCondition}
                  onPress={getCallback(columnKey)}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  value={rowData[columnKey]}
                  isLastCell={isLastCell}
                  width={width}
                />
              );
            }

            default: {
              return (
                <Cell
                  key={columnKey}
                  value={rowData[columnKey]}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }
          }
        }),
      [isFinalised, rowState, rowData, rowIndex, columns]
    );

    return (
      <Row
        onPress={onPress}
        style={rowStyle}
        renderCells={renderCells}
        rowKey={rowKey}
        rowData={rowData}
        rowState={rowState}
        rowIndex={rowIndex}
        debug
      />
    );
  }
);

DataTableRow.defaultProps = {
  isFinalised: false,
  getCallback: null,
  onPress: null,
  rowState: null,
  getCellError: () => null,
  isValidated: true,
};

DataTableRow.propTypes = {
  onPress: PropTypes.func,
  rowData: PropTypes.object.isRequired,
  rowState: PropTypes.object,
  rowKey: PropTypes.string.isRequired,
  columns: PropTypes.array.isRequired,
  isFinalised: PropTypes.bool,
  getCallback: PropTypes.func,
  rowIndex: PropTypes.number.isRequired,
  getCellError: PropTypes.func,
  isValidated: PropTypes.bool,
};

export default DataTableRow;
<commit_msg>Remove debug prop<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';

import currency from '../../localization/currency';

import { ROW_BLUE, dataTableStyles, SUSSOL_ORANGE } from '../../globalStyles';

import Row from './Row';
import Cell from './Cell';

import CheckableCell from './CheckableCell';
import TouchableCell from './TouchableCell';
import DropDownCell from '../DropDownCell';
import TextInputCell from './TextInputCell';
import { ExpiryDateInput } from '../ExpiryDateInput';
import {
  CheckedIcon,
  UncheckedIcon,
  DisabledCheckedIcon,
  DisabledUncheckedIcon,
  ChevronRightIcon,
  HistoryIcon,
  PencilIcon,
  HazardIcon,
  BookIcon,
} from '../icons';

import { COLUMN_TYPES, COLUMN_KEYS } from '../../pages/dataTableUtilities';
import { generalStrings, tableStrings } from '../../localization';
import { formatStatus, formatDate } from '../../utilities';
import { formatType } from '../../utilities/formatStatus';
import { useDebounce } from '../../hooks';

/**
 * Wrapper component for a mSupply DataTable page row.
 * Wraps Row component with a contained RenderCells, which
 * will generate the appropriate cell for a given columnKey.
 * Doesn't need to be used, but is a convenience component.
 *
 * @param {Object} rowData     Data object for a row i.e. ItemBatch object
 * @param {Object} rowState    State object for a row, see: Row.js
 * @param {Object} style       Style object the be passed to inner Row
 * @param {String} rowKey      Unique key for a row
 * @param {Array}  columns     Array of column objects, see: columns.js
 * @param {Bool}   isFinalised Boolean indicating if the DataTable page is finalised.
 * @param {Func}   rowIndex    index of this row.
 * @param {Func}   onPress     On press callback for the row itself.
 * @param {Func}   getCallback Function to return a cells callback
 *                             (colKey, propName) => callback
 */
const DataTableRow = React.memo(
  ({
    rowData,
    rowState,
    rowKey,
    columns,
    isFinalised,
    getCallback,
    onPress,
    rowIndex,
    getCellError,
    isValidated,
  }) => {
    const {
      cellText,
      cellContainer,
      warningCellContainer,
      errorCellContainer,
      touchableCellContainer,
      editableCellText,
      editableCellTextView,
      editableCellUnfocused,
      selectedRow: selectedRowStyle,
      alternateRow: alternateRowStyle,
      row: basicRowStyle,
      iconCell,
    } = dataTableStyles;

    const { isSelected = false } = rowState || {};

    const [hasFocus, setFocused] = React.useState(false);
    const focus = useDebounce(shouldFocus => setFocused(shouldFocus), 50, false);

    const rowStyle = useMemo(
      () => ({
        ...(rowIndex % 2 === 0 ? alternateRowStyle : basicRowStyle),
        ...(isSelected && selectedRowStyle),
        ...(!isValidated && { borderColor: SUSSOL_ORANGE, borderWidth: 1, borderRadius: 5 }),
        ...(hasFocus && { backgroundColor: ROW_BLUE }),
      }),
      [rowIndex, isSelected, isValidated, hasFocus]
    );

    // Callback for rendering a row of cells.
    const renderCells = useCallback(
      () =>
        // Map each column to an appropriate cell for a given row.
        columns.map(({ key: columnKey, type, width, alignText, icon }, index) => {
          // Indicator if the right hand border should be removed from styles for this cell.
          const isLastCell = index === columns.length - 1;

          // This cell is disabled if:
          // - the page is finalised.
          // - the row has been explicitly set as disabled.
          const rowIsDisabled = rowState?.isDisabled ?? false;
          const isDisabled = isFinalised || rowIsDisabled;

          // Alignment of this particular column. Default to left hand ide.
          const cellAlignment = alignText || 'left';

          switch (type) {
            case COLUMN_TYPES.EDITABLE_STRING:
            case COLUMN_TYPES.EDITABLE_NUMERIC: {
              const { isVaccine, hasBeenCounted } = rowData;

              // Placeholder values which are determined on a per row basis. For example, display
              // "Not counted" as a placeholder on a stocktake row. Placeholders are used under
              // the condition [placeholder && !value] which can be a source of bugs with JS
              // falsey values. I.e. if a value is 0, the placeholder will display rather than
              // the value.
              const extraPlaceholders = {
                [COLUMN_KEYS.COUNTED_TOTAL_QUANTITY]: hasBeenCounted
                  ? ''
                  : tableStrings.not_counted,
              };

              // Extra disabled conditions for columns which can be determined per row. For example,
              // only allow the doses column for vaccine items.
              const disabledCondition = {
                [COLUMN_KEYS.DOSES]: !isVaccine,
              };

              // Extra text which can be displayed for a column on a per row basis when the
              // cell is disabled. For example display N/A on a row for the doses column rather
              // than the underlying value "0".
              const disabledText = {
                [COLUMN_KEYS.DOSES]: isVaccine ? '' : generalStrings.not_available,
              };

              const value = disabledText[columnKey] ? disabledText[columnKey] : rowData[columnKey];
              const placeholder = extraPlaceholders[columnKey] ?? '';

              const inputIsDisabled = isDisabled || !!disabledCondition[columnKey];

              const cellErrors = getCellError?.(rowData, columnKey);
              const alternateCellStyleLookup = {
                warning: warningCellContainer,
                error: errorCellContainer,
              };
              const cellContainerStyle = alternateCellStyleLookup[cellErrors] ?? cellContainer;

              return (
                <TextInputCell
                  onFocus={() => focus(true)}
                  onBlur={() => focus(false)}
                  key={columnKey}
                  value={value}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onChangeText={getCallback(columnKey)}
                  isDisabled={inputIsDisabled}
                  width={width}
                  viewStyle={cellContainerStyle[cellAlignment]}
                  textViewStyle={editableCellTextView}
                  isLastCell={isLastCell}
                  keyboardType={type === COLUMN_TYPES.EDITABLE_NUMERIC ? 'numeric' : 'default'}
                  textInputStyle={cellText[cellAlignment]}
                  textStyle={editableCellUnfocused[cellAlignment]}
                  cellTextStyle={editableCellText}
                  rowIndex={rowIndex}
                  placeholder={placeholder}
                />
              );
            }
            case COLUMN_TYPES.EDITABLE_EXPIRY_DATE:
              return (
                <ExpiryDateInput
                  onFocus={() => focus(true)}
                  onBlur={() => focus(false)}
                  key={columnKey}
                  value={rowData[columnKey]}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onEndEditing={getCallback(columnKey)}
                  isDisabled={isDisabled}
                  width={width}
                  isLastCell={isLastCell}
                  rowIndex={rowIndex}
                />
              );

            case COLUMN_TYPES.CHECKABLE:
              return (
                <CheckableCell
                  key={columnKey}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  isChecked={rowState && rowState.isSelected}
                  isDisabled={isDisabled}
                  CheckedComponent={CheckedIcon}
                  UncheckedComponent={UncheckedIcon}
                  DisabledCheckedComponent={DisabledCheckedIcon}
                  DisabledUncheckedComponent={DisabledUncheckedIcon}
                  onCheck={getCallback(columnKey, 'onCheck')}
                  onUncheck={getCallback(columnKey, 'onUncheck')}
                  containerStyle={touchableCellContainer}
                  width={width}
                  isLastCell={isLastCell}
                />
              );

            case COLUMN_TYPES.CURRENCY: {
              const value = currency(rowData[columnKey]).format(false);

              return (
                <Cell
                  key={columnKey}
                  value={value}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.STRING: {
              const value = rowData[columnKey];
              let displayValue = columnKey === 'status' ? formatStatus(value) : value;
              displayValue = columnKey === 'type' ? formatType(displayValue) : displayValue;

              return (
                <Cell
                  key={columnKey}
                  value={displayValue}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.NUMERIC: {
              // Special condition for stocktake difference cells.
              // Use the placeholder 'Not counted' when a stocktake item or batch
              // has not been counted yet.
              const value =
                columnKey === COLUMN_KEYS.DIFFERENCE && !rowData.hasBeenCounted
                  ? generalStrings.not_available
                  : Math.round(rowData[columnKey]);

              const cellErrors = getCellError?.(rowData, columnKey);
              const alternateCellStyleLookup = {
                warning: warningCellContainer,
                error: errorCellContainer,
              };
              const cellContainerStyle = alternateCellStyleLookup[cellErrors] ?? cellContainer;

              return (
                <Cell
                  key={columnKey}
                  value={value}
                  width={width}
                  viewStyle={cellContainerStyle[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }

            case COLUMN_TYPES.DATE:
              return (
                <Cell
                  key={columnKey}
                  value={formatDate(rowData[columnKey], 'll') ?? generalStrings.not_available}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );

            case COLUMN_TYPES.ICON: {
              const { hasBreached, isVaccine } = rowData ?? {};

              const icons = {
                chevron_right: ChevronRightIcon,
                history: () => <HistoryIcon color={SUSSOL_ORANGE} />,
                pencil: () => <PencilIcon color={SUSSOL_ORANGE} />,
                breach: () => (hasBreached ? <HazardIcon color={SUSSOL_ORANGE} /> : null),
                book: () => <BookIcon color={SUSSOL_ORANGE} />,
              };

              const disabledConditions = {
                [COLUMN_KEYS.HAS_BREACHED]: !(hasBreached && isVaccine),
              };

              const isEditReadOnlyRecord =
                (columnKey === COLUMN_KEYS.PATIENT_EDIT ||
                  columnKey === COLUMN_KEYS.PRESCRIBER_EDIT) &&
                !rowData.isEditable;
              const iconComponent = isEditReadOnlyRecord ? icons.book : icons[icon];

              const isDisabledIcon =
                (isDisabled || disabledConditions[columnKey]) && columnKey !== COLUMN_KEYS.BATCH;

              return (
                <TouchableCell
                  key={columnKey}
                  renderChildren={iconComponent}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  onPress={getCallback(columnKey)}
                  width={width}
                  isLastCell={isLastCell}
                  isDisabled={isDisabledIcon}
                  containerStyle={iconCell}
                />
              );
            }

            case COLUMN_TYPES.DROP_DOWN: {
              const { isVaccine = false, hasVariance = false } = rowData ?? {};

              const disabledConditions = {
                [COLUMN_KEYS.CURRENT_VVM_STATUS]: !isVaccine,
                [COLUMN_KEYS.REASON_TITLE]: !hasVariance,
              };

              const extraDisabledCondition = disabledConditions[columnKey];

              return (
                <DropDownCell
                  key={columnKey}
                  isDisabled={isDisabled || extraDisabledCondition}
                  onPress={getCallback(columnKey)}
                  rowKey={rowKey}
                  columnKey={columnKey}
                  value={rowData[columnKey]}
                  isLastCell={isLastCell}
                  width={width}
                />
              );
            }

            default: {
              return (
                <Cell
                  key={columnKey}
                  value={rowData[columnKey]}
                  width={width}
                  viewStyle={cellContainer[cellAlignment]}
                  textStyle={cellText[cellAlignment]}
                  isLastCell={isLastCell}
                />
              );
            }
          }
        }),
      [isFinalised, rowState, rowData, rowIndex, columns]
    );

    return (
      <Row
        onPress={onPress}
        style={rowStyle}
        renderCells={renderCells}
        rowKey={rowKey}
        rowData={rowData}
        rowState={rowState}
        rowIndex={rowIndex}
      />
    );
  }
);

DataTableRow.defaultProps = {
  isFinalised: false,
  getCallback: null,
  onPress: null,
  rowState: null,
  getCellError: () => null,
  isValidated: true,
};

DataTableRow.propTypes = {
  onPress: PropTypes.func,
  rowData: PropTypes.object.isRequired,
  rowState: PropTypes.object,
  rowKey: PropTypes.string.isRequired,
  columns: PropTypes.array.isRequired,
  isFinalised: PropTypes.bool,
  getCallback: PropTypes.func,
  rowIndex: PropTypes.number.isRequired,
  getCellError: PropTypes.func,
  isValidated: PropTypes.bool,
};

export default DataTableRow;
<|endoftext|>