<commit_before>var web3 = require("web3");
var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var provision = require("./provision");

var Contracts = {
  resolve(root, callback) {
    var imported = {};

    var import_file = function(file) {
      var code = fs.readFileSync(file, "utf-8");

      // Remove comments
      code = code.replace(/(\/\/.*(\n|$))/g, "");
      code = code.replace(/(\/\*(.|\n)*?\*\/)/g, "");
      code = code.replace("*/", ""); // Edge case.

      // Perform imports.
      code = code.replace(/import ('|")[^'"]+('|");/g, function(match) {
        match = match.replace(/'/g, '"');
        var import_name = match.split('"')[1];
        var import_path = path.dirname(file) + "/" + import_name + ".sol";

        // Don't import the same thing twice if there are two of the same dependency.
        if (imported[import_name] == true) {
          return "";
        }

        if (!fs.existsSync(import_path)) {
          throw `Could not find source for '${import_name} from ${file}'. Expected: ${import_path}`
        }

        imported[import_name] = true;

        return import_file(import_path) + "\n\n";
      });
      return code;
    };

    try {
      callback(null, import_file(root));
    } catch(e) {
      callback(e);
    }
  },

  // Support the breaking change that made sendTransaction return a transaction
  // hash instead of an address hash when committing a new contract.
  get_contract_address(config, address_or_tx, callback) {
    if (address_or_tx.length == 42) {
      callback(null, address_or_tx);
      return;
    }

    var attempts = 0;
    var max_attempts = 120;

    var interval = null;
    var verify = function() {
      // Call the method via the provider directly as it hasn't yet been
      // implemented in web3.
      web3.currentProvider.sendAsync({
        jsonrpc: "2.0",
        method: "eth_getTransactionReceipt",
        params:[address_or_tx],
        id: new Date().getTime()
      }, function(err, result) {
        if (err != null) {
          callback(err);
          return;
        }

        result = result.result;

        // Gotta love inconsistent responses.
        if (result != null && result != "" && result != "0x") {
          clearInterval(interval);
          callback(null, result.contractAddress);
          return;
        }

        attempts += 1;

        if (attempts >= max_attempts) {
          clearInterval(interval);
          callback(new Error(`Contracts not deployed after ${attempts} seconds!`));
        }
      });
    };

    interval = setInterval(verify, 1000);
  },

  check_for_valid_compiler(file, callback) {
    var compiler_name = null;

    if (path.extname(file) == ".sol") {
      compiler_name = "solidity";
    }

    if (compiler_name == null) {
      callback(new Error(`Compiler for ${path.extname(file)} not yet supported by Truffle. We hope to support every compiler eventually. Express your interested by filing a bug report on Github.`));
      return;
    }

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

      // Make all compiler names lower case for comparison.
      result = result.map(function(x) {
        return x.toLowerCase();
      });

      if (result.indexOf(compiler_name) < 0) {
        callback(new Error(`Your RPC client doesn't support compiling files with the ${path.extname(file)} extension. Please make sure you have the relevant compilers installed and your RPC client is configured correctly. The compilers supported by your RPC client are: [${result.join(', ')}]`));
        return;
      }

      callback();
    });
  },

  compile_all(config, callback) {
    async.mapSeries(Object.keys(config.contracts.classes), (key, finished) => {
      var contract = config.contracts.classes[key];
      var source = contract.source;

      var display_name = source.substring(source.lastIndexOf("/") + 1);
      if (!config.grunt.option("quiet-deploy")) {
        console.log(`Compiling ${display_name}...`);
      }

      this.check_for_valid_compiler(source, (err) => {
        if (err != null) {
          finished(err);
          return;
        }

        this.resolve(source, function(err, code) {
          if (err != null) {
            finished(err);
            return;
          }

          web3.eth.compile.solidity(code, function(err, result) {
            if (err != null) {
              finished(err, result);
              return;
            }

            contract["binary"] = result[key].code;
            contract["abi"] = result[key].info.abiDefinition;
            finished(null, contract);
          });
        });
      });
    }, function(err, result) {
      if (err != null) {
        console.log("");
        console.log(err.message);
        console.log("");
        console.log("Hint: Some clients don't send helpful error messages through the RPC. See client logs for more details.");
        err = new Error("Compilation failed. See above.");
      }
      callback(err)
    });
  },

  write_contracts(config, description="contracts", callback) {
    mkdirp(config.environments.current.directory, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "." + config.environments.current.contracts_filename.replace(config.working_dir, "");
      if (!config.grunt.option("quiet-deploy")) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      fs.writeFileSync(config.environments.current.contracts_filename, JSON.stringify(config.contracts.classes, null, 2), {flag: "w+"});
      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        config.test_connection(function(error, coinbase) {
          if (error != null) {
            callback(new Error("Could not connect to your Ethereum client. Truffle uses your Ethereum client to compile contracts. Please ensure your client is running and can compile the all contracts within the contracts directory."));
          } else {
            c();
          }
        });
      },
      (c) => {
        this.compile_all(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },

  deploy(config, compile=true, done_deploying) {
    var coinbase = null;

    async.series([
      (c) => {
        web3.eth.getCoinbase(function(error, result) {
          coinbase = result;
          c(error, result);
        });
      },
      (c) => {
        if (compile == true) {
          this.compile_all(config, c);
        } else {
          c();
        }
      },
      (c) => {
        // Put them on the network
        var provisioner = provision.asModule(config);
        provisioner.provision_contracts(global);

        async.mapSeries(config.app.resolved.deploy, (key, callback) => {
          var contract = global[key]; //config.contracts.classes[key];
          var contract_class = config.contracts.classes[key];

          if (contract == null) {
            callback(new Error(`Could not find contract '${key}' for deployment. Check app.json.`));
            return;
          }

          var display_name = contract_class.source.substring(contract_class.source.lastIndexOf("/") + 1);
          if (!config.grunt.option("quiet-deploy")) {
            console.log(`Sending ${display_name} to the network...`);
          }

          contract.new({
            from: coinbase,
            gas: 3141592,
            gasPrice: 1000000000000 // I'm not sure why this is so high. geth made me do it.
          }).then(function(instance) {
            contract_class.address = instance.address;
            callback(null, contract_class);
          }).catch(callback);

          // web3.eth.sendTransaction({
          //   from: coinbase,
          //
          //   data: contract.binary
          // }, (err, result) => {
          //
          //
          //   this.get_contract_address(config, result, function(err, address) {
          //     contract.address = address;
          //     callback(err, contract);
          //   });
          // });

        }, function(err, results) {
          if (err != null) {
            console.log("ERROR sending contract:");
            c(err);
          } else {
            c();
          }
        });
      },
      (c) => {
        // Verifying deployment of contracts
        if (!config.grunt.option("quiet-deploy")) {
          console.log("Verifying deployment...");
        }

        var attempts = 0;
        var max_attempts = 120;

        var interval = null;
        var verify = function() {
          var last = config.app.resolved.deploy[config.app.resolved.deploy.length - 1];
          var contract = config.contracts.classes[last]

          web3.eth.getCode(contract.address, function(err, response) {
            // Ignore errors.
            if (err != null) {
              return;
            }

            if (response != "" && response != "0x") {
              clearInterval(interval);
              c();
              return;
            }

            attempts += 1;

            if (attempts >= max_attempts) {
              clearInterval(interval);
              c(new Error(`Contracts not deployed after ${attempts} seconds!
                           This might be because your contract's constructor
                           is encountering a runtime error.`));
            }
          });
        };

        interval = setInterval(verify, 1000);
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      }

      this.write_contracts(config, "contracts and deployed addresses", done_deploying);
    });
  }
}

module.exports = Contracts;
<commit_msg>Remove deployment verification as Pudding/web3 does that already.<commit_after>var web3 = require("web3");
var async = require("async");
var fs = require("fs");
var mkdirp = require("mkdirp");
var path = require("path");
var provision = require("./provision");

var Contracts = {
  resolve(root, callback) {
    var imported = {};

    var import_file = function(file) {
      var code = fs.readFileSync(file, "utf-8");

      // Remove comments
      code = code.replace(/(\/\/.*(\n|$))/g, "");
      code = code.replace(/(\/\*(.|\n)*?\*\/)/g, "");
      code = code.replace("*/", ""); // Edge case.

      // Perform imports.
      code = code.replace(/import ('|")[^'"]+('|");/g, function(match) {
        match = match.replace(/'/g, '"');
        var import_name = match.split('"')[1];
        var import_path = path.dirname(file) + "/" + import_name + ".sol";

        // Don't import the same thing twice if there are two of the same dependency.
        if (imported[import_name] == true) {
          return "";
        }

        if (!fs.existsSync(import_path)) {
          throw `Could not find source for '${import_name} from ${file}'. Expected: ${import_path}`
        }

        imported[import_name] = true;

        return import_file(import_path) + "\n\n";
      });
      return code;
    };

    try {
      callback(null, import_file(root));
    } catch(e) {
      callback(e);
    }
  },

  // Support the breaking change that made sendTransaction return a transaction
  // hash instead of an address hash when committing a new contract.
  get_contract_address(config, address_or_tx, callback) {
    if (address_or_tx.length == 42) {
      callback(null, address_or_tx);
      return;
    }

    var attempts = 0;
    var max_attempts = 120;

    var interval = null;
    var verify = function() {
      // Call the method via the provider directly as it hasn't yet been
      // implemented in web3.
      web3.currentProvider.sendAsync({
        jsonrpc: "2.0",
        method: "eth_getTransactionReceipt",
        params:[address_or_tx],
        id: new Date().getTime()
      }, function(err, result) {
        if (err != null) {
          callback(err);
          return;
        }

        result = result.result;

        // Gotta love inconsistent responses.
        if (result != null && result != "" && result != "0x") {
          clearInterval(interval);
          callback(null, result.contractAddress);
          return;
        }

        attempts += 1;

        if (attempts >= max_attempts) {
          clearInterval(interval);
          callback(new Error(`Contracts not deployed after ${attempts} seconds!`));
        }
      });
    };

    interval = setInterval(verify, 1000);
  },

  check_for_valid_compiler(file, callback) {
    var compiler_name = null;

    if (path.extname(file) == ".sol") {
      compiler_name = "solidity";
    }

    if (compiler_name == null) {
      callback(new Error(`Compiler for ${path.extname(file)} not yet supported by Truffle. We hope to support every compiler eventually. Express your interested by filing a bug report on Github.`));
      return;
    }

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

      // Make all compiler names lower case for comparison.
      result = result.map(function(x) {
        return x.toLowerCase();
      });

      if (result.indexOf(compiler_name) < 0) {
        callback(new Error(`Your RPC client doesn't support compiling files with the ${path.extname(file)} extension. Please make sure you have the relevant compilers installed and your RPC client is configured correctly. The compilers supported by your RPC client are: [${result.join(', ')}]`));
        return;
      }

      callback();
    });
  },

  compile_all(config, callback) {
    async.mapSeries(Object.keys(config.contracts.classes), (key, finished) => {
      var contract = config.contracts.classes[key];
      var source = contract.source;

      var display_name = source.substring(source.lastIndexOf("/") + 1);
      if (!config.grunt.option("quiet-deploy")) {
        console.log(`Compiling ${display_name}...`);
      }

      this.check_for_valid_compiler(source, (err) => {
        if (err != null) {
          finished(err);
          return;
        }

        this.resolve(source, function(err, code) {
          if (err != null) {
            finished(err);
            return;
          }

          web3.eth.compile.solidity(code, function(err, result) {
            if (err != null) {
              finished(err, result);
              return;
            }

            contract["binary"] = result[key].code;
            contract["abi"] = result[key].info.abiDefinition;
            finished(null, contract);
          });
        });
      });
    }, function(err, result) {
      if (err != null) {
        console.log("");
        console.log(err.message);
        console.log("");
        console.log("Hint: Some clients don't send helpful error messages through the RPC. See client logs for more details.");
        err = new Error("Compilation failed. See above.");
      }
      callback(err)
    });
  },

  write_contracts(config, description="contracts", callback) {
    mkdirp(config.environments.current.directory, function(err, result) {
      if (err != null) {
        callback(err);
        return;
      }

      var display_directory = "." + config.environments.current.contracts_filename.replace(config.working_dir, "");
      if (!config.grunt.option("quiet-deploy")) {
        console.log(`Writing ${description} to ${display_directory}`);
      }

      fs.writeFileSync(config.environments.current.contracts_filename, JSON.stringify(config.contracts.classes, null, 2), {flag: "w+"});
      callback();
    });
  },

  compile(config, callback) {
    async.series([
      (c) => {
        config.test_connection(function(error, coinbase) {
          if (error != null) {
            callback(new Error("Could not connect to your Ethereum client. Truffle uses your Ethereum client to compile contracts. Please ensure your client is running and can compile the all contracts within the contracts directory."));
          } else {
            c();
          }
        });
      },
      (c) => {
        this.compile_all(config, c);
      },
      (c) => {
        this.write_contracts(config, "contracts", c);
      }
    ], callback);
  },

  deploy(config, compile=true, done_deploying) {
    var coinbase = null;

    async.series([
      (c) => {
        web3.eth.getCoinbase(function(error, result) {
          coinbase = result;
          c(error, result);
        });
      },
      (c) => {
        if (compile == true) {
          this.compile_all(config, c);
        } else {
          c();
        }
      },
      (c) => {
        // Put them on the network
        var provisioner = provision.asModule(config);
        provisioner.provision_contracts(global);

        async.mapSeries(config.app.resolved.deploy, (key, callback) => {
          var contract = global[key]; //config.contracts.classes[key];
          var contract_class = config.contracts.classes[key];

          if (contract == null) {
            callback(new Error(`Could not find contract '${key}' for deployment. Check app.json.`));
            return;
          }

          var display_name = contract_class.source.substring(contract_class.source.lastIndexOf("/") + 1);
          if (!config.grunt.option("quiet-deploy")) {
            console.log(`Sending ${display_name} to the network...`);
          }

          contract.new({
            from: coinbase,
            gas: 3141592,
            gasPrice: 1000000000000 // I'm not sure why this is so high. geth made me do it.
          }).then(function(instance) {
            contract_class.address = instance.address;
            callback(null, contract_class);
          }).catch(callback);

        }, function(err, results) {
          if (err != null) {
            console.log("ERROR sending contract:");
            c(err);
          } else {
            c();
          }
        });
      }
    ], (err) => {
      if (err != null) {
        done_deploying(err);
        return;
      }

      this.write_contracts(config, "contracts and deployed addresses", done_deploying);
    });
  }
}

module.exports = Contracts;
<|endoftext|>


<commit_before>var _ = require('lodash'),
    Q = require('q'),
    jsonPath = require('JSONPath'),
    moment = require('moment'),
    program = require('commander'),
    utils = require('./utils'),
    validator = require('validator');

program
  .version(utils.getVersion('..'))
  .option('--api-endpoint <url>', 'set the API endpoint')
  .option('--token <token>', 'set the token');
//.option('--dry-run', 'do not execute actions')
//.option('-D, --debug', 'enable debug mode')

program._events.version = null;
program
  .command('version')
  .description('show the version information')
  .action(function() {
    console.log('Client version: ' + utils.getVersion('..'));
    console.log('Node.js version (client): ' + process.version);
    console.log('OS/Arch (client): ' + process.platform + '/' + process.arch);
    // FIXME: add information about server
  });

program
  .command('ls <type> [conditions...]')
  .alias('select')
  .description('list objects')
  .option('--no-trunc', "don't truncate output")
  .option('-f, --field <field>', 'fields to print', utils.collect, [])
  .option('-q, --quiet', 'only print ids')
  .option('-l, --limit <max_results>', 'limit results to <max_results> items', 50)
  .option('-p, --page <page>', 'use page <page>', 1)
  .option('-a, --all', 'automatically walk paginated results')
  .option('-o, --order <field>', 'order by <field>', '-_updated')
  .action(function(type, conditions, options) {
    var client = utils.newApi(options);

    type = type || '';
    var query = {};
    if (conditions.length) {
      query['where'] = JSON.stringify(utils.castFields(type, conditions));
    }
    if (options.limit) {
      query['max_results'] = options.limit;
    }
    if (options.page) {
      query['page'] = options.page;
    }

    if (options.order) {
      query['sort'] = options.order;
    }

    var url = '/' + type + '?';
    _.forEach(query, function(value, key) {
      url += key + '=' + value + '&';
    });

    var getter = options.all ? 'all' : 'get';
    client[getter](url)
      .then(function(res) {
        if (!res.body._items.length) {
          console.error('No entries');
          return;
        }
        var items = res.body._items;

        // sorting items
        items = _.sortByOrder(items, ['_created'], [false]);

        // quiet mode
        if (options.quiet) {
          console.log(_.pluck(items, '_id').join('\n'));
          return;
        }

        // get all keys
        var keys = _.sortBy(_.difference(
          _.union(_.flatten(_.map(items, _.keys))),
          ['_links', '_etag']
        ));
        if (options.field.length) {
          keys = _.intersection(keys, options.field);
          var difference = _.difference(options.field, keys);
          if (difference.length) {
            utils.error('Unknown fields: ' + difference);
          }
        }
        if (!keys.length) {
          utils.panic('No fields to print');
        }

        var table = utils.newTable({
          head: keys
        });

        _.forEach(items, function(item) {
          var row = [];
          _.forEach(keys, function(key) {

            switch (key) {

              // Dates
            case '_updated':
            case '_created':
              row.push(moment(new Date(item[key])).fromNow());
              break;

              // UUID
            case '_id':
              row.push(utils.truncateUUID(item[key], options.trunc));
              break;

            default:
              var value = (item[key] || '').toString();

              if (validator.isUUID(value)) {
                value = utils.truncateUUID(value, options.trunc);
              }

              if (value.substring(0, 4) == '$2a$') {
                value = '<blowfish>';
              }

              row.push(value);
              break;
            }

          });
          table.push(row);
        });

        console.log(table.toString());
        var meta = res.body._meta;
        if (meta.max_results < meta.total || options.all) {
          console.log('Displaying items: ' + (meta.max_results * (meta.page - 1) + 1) + '-' + (meta.max_results * meta.page) + '/' + meta.total);
        }
      })
      .catch(utils.panic);
  });

program
  .command('cat <item>')
  .alias('show')
  .description('show object')
  .option('--no-trunc', "don't truncate output")
  .option('-f, --format <format>', 'format the output using the given template')
  .action(function(item, options) {
    var client = utils.newApi(options);

    // FIXME: resolve nested fields
    utils.searchItems(item, client, function(items) {
      _.forEach(items, function(item) {
        if (options.trunc) {
          delete item._links;
          delete item._etag;
        }
        if (options.format) {
          var parsed = jsonPath.eval(item, '$' + options.format);
          if (typeof(parsed) === 'object' && parsed.length === 1) {
            console.log(parsed[0]);
          } else {
            console.log(parsed);
          }
        } else {
          console.log(JSON.stringify(item, null, 2));
        }
      });
    }, utils.panic);
  });

program
  .command('rm <item>')
  .alias('delete')
  .description('remove an item')
  .action(function(item, options) {
    var client = utils.newApi(options);

    // FIXME: add warning !

    utils.searchItems(item, client, function(items) {
      client.delete(items[0]).then(function(res) {
        console.log('done');
      }).catch(utils.panic);
    }, utils.panic);
  });

program
  .command('touch <type> [fields...]')
  .alias('add')
  .description('create an item')
  .action(function(type, fields, options) {
    var client = utils.newApi(options);

    var input = utils.castFields(type, fields);

    client.post('/' + type, input).then(function(res) {
      console.log(res.body._id);
    }).catch(utils.panic);
  });

program
  .command('update <item> <fields...>')
  .alias('patch')
  .description('update an item')
  .action(function(item, fields, options) {
    var client = utils.newApi(options);

    utils.searchItems(item, client, function(items) {
      var input = utils.castFields(items[0], fields);
      client.patch(items[0], input).then(function(res) {
        console.log(res.body._id);
      }).catch(utils.panic);
    }, utils.panic);
  });

module.exports = program;

module.exports.run = function() {
  program.parse(process.argv);
  if (!process.argv.slice(2).length) {
    program.outputHelp();
  }
};
<commit_msg>Raise an error when trying to cat an incomplete item path<commit_after>var _ = require('lodash'),
    Q = require('q'),
    jsonPath = require('JSONPath'),
    moment = require('moment'),
    program = require('commander'),
    utils = require('./utils'),
    validator = require('validator');

program
  .version(utils.getVersion('..'))
  .option('--api-endpoint <url>', 'set the API endpoint')
  .option('--token <token>', 'set the token');
//.option('--dry-run', 'do not execute actions')
//.option('-D, --debug', 'enable debug mode')

program._events.version = null;
program
  .command('version')
  .description('show the version information')
  .action(function() {
    console.log('Client version: ' + utils.getVersion('..'));
    console.log('Node.js version (client): ' + process.version);
    console.log('OS/Arch (client): ' + process.platform + '/' + process.arch);
    // FIXME: add information about server
  });

program
  .command('ls <type> [conditions...]')
  .alias('select')
  .description('list objects')
  .option('--no-trunc', "don't truncate output")
  .option('-f, --field <field>', 'fields to print', utils.collect, [])
  .option('-q, --quiet', 'only print ids')
  .option('-l, --limit <max_results>', 'limit results to <max_results> items', 50)
  .option('-p, --page <page>', 'use page <page>', 1)
  .option('-a, --all', 'automatically walk paginated results')
  .option('-o, --order <field>', 'order by <field>', '-_updated')
  .action(function(type, conditions, options) {
    var client = utils.newApi(options);

    type = type || '';
    var query = {};
    if (conditions.length) {
      query['where'] = JSON.stringify(utils.castFields(type, conditions));
    }
    if (options.limit) {
      query['max_results'] = options.limit;
    }
    if (options.page) {
      query['page'] = options.page;
    }

    if (options.order) {
      query['sort'] = options.order;
    }

    var url = '/' + type + '?';
    _.forEach(query, function(value, key) {
      url += key + '=' + value + '&';
    });

    var getter = options.all ? 'all' : 'get';
    client[getter](url)
      .then(function(res) {
        if (!res.body._items.length) {
          console.error('No entries');
          return;
        }
        var items = res.body._items;

        // sorting items
        items = _.sortByOrder(items, ['_created'], [false]);

        // quiet mode
        if (options.quiet) {
          console.log(_.pluck(items, '_id').join('\n'));
          return;
        }

        // get all keys
        var keys = _.sortBy(_.difference(
          _.union(_.flatten(_.map(items, _.keys))),
          ['_links', '_etag']
        ));
        if (options.field.length) {
          keys = _.intersection(keys, options.field);
          var difference = _.difference(options.field, keys);
          if (difference.length) {
            utils.error('Unknown fields: ' + difference);
          }
        }
        if (!keys.length) {
          utils.panic('No fields to print');
        }

        var table = utils.newTable({
          head: keys
        });

        _.forEach(items, function(item) {
          var row = [];
          _.forEach(keys, function(key) {

            switch (key) {

              // Dates
            case '_updated':
            case '_created':
              row.push(moment(new Date(item[key])).fromNow());
              break;

              // UUID
            case '_id':
              row.push(utils.truncateUUID(item[key], options.trunc));
              break;

            default:
              var value = (item[key] || '').toString();

              if (validator.isUUID(value)) {
                value = utils.truncateUUID(value, options.trunc);
              }

              if (value.substring(0, 4) == '$2a$') {
                value = '<blowfish>';
              }

              row.push(value);
              break;
            }

          });
          table.push(row);
        });

        console.log(table.toString());
        var meta = res.body._meta;
        if (meta.max_results < meta.total || options.all) {
          console.log('Displaying items: ' + (meta.max_results * (meta.page - 1) + 1) + '-' + (meta.max_results * meta.page) + '/' + meta.total);
        }
      })
      .catch(utils.panic);
  });

program
  .command('cat <item>')
  .alias('show')
  .description('show object')
  .option('--no-trunc', "don't truncate output")
  .option('-f, --format <format>', 'format the output using the given template')
  .action(function(item, options) {
    var client = utils.newApi(options);

    // FIXME: resolve nested fields
    if (item[item.length - 1] == '/') {
      utils.panic("invalid item '" + item + "'");
    }
    utils.searchItems(item, client, function(items) {
      _.forEach(items, function(item) {
        if (options.trunc) {
          delete item._links;
          delete item._etag;
        }
        if (options.format) {
          var parsed = jsonPath.eval(item, '$' + options.format);
          if (typeof(parsed) === 'object' && parsed.length === 1) {
            console.log(parsed[0]);
          } else {
            console.log(parsed);
          }
        } else {
          console.log(JSON.stringify(item, null, 2));
        }
      });
    }, utils.panic);
  });

program
  .command('rm <item>')
  .alias('delete')
  .description('remove an item')
  .action(function(item, options) {
    var client = utils.newApi(options);

    // FIXME: add warning !

    utils.searchItems(item, client, function(items) {
      client.delete(items[0]).then(function(res) {
        console.log('done');
      }).catch(utils.panic);
    }, utils.panic);
  });

program
  .command('touch <type> [fields...]')
  .alias('add')
  .description('create an item')
  .action(function(type, fields, options) {
    var client = utils.newApi(options);

    var input = utils.castFields(type, fields);

    client.post('/' + type, input).then(function(res) {
      console.log(res.body._id);
    }).catch(utils.panic);
  });

program
  .command('update <item> <fields...>')
  .alias('patch')
  .description('update an item')
  .action(function(item, fields, options) {
    var client = utils.newApi(options);

    utils.searchItems(item, client, function(items) {
      var input = utils.castFields(items[0], fields);
      client.patch(items[0], input).then(function(res) {
        console.log(res.body._id);
      }).catch(utils.panic);
    }, utils.panic);
  });

module.exports = program;

module.exports.run = function() {
  program.parse(process.argv);
  if (!process.argv.slice(2).length) {
    program.outputHelp();
  }
};
<|endoftext|>


<commit_before>/******************************************************************************
 * Module dependencies
 */
var express             = require('express')
  , __                  = require('express-resource')
  , async               = require('async')
  , path                = require('path')
  , _                   = require('underscore')
  , lingo               = require('lingo')
  ;

//project related dependencies
var asyncFs             = require('./util/async_fs')
  , dadt                = require('./util/dadt')
  , config              = require('./config')

  , CONTROLLER_NAME_REGEXP   = config.CONTROLLER_NAME_REGEXP
  ;

/******************************************************************************
 * Rocket template name space
 */
var rocket_tmpl         = {};

    rocket_tmpl.header  = function() {

      var args            = Array.prototype.slice.call(arguments)
        , controller_name = args.shift().replace(/[.,-;\s]+/g, '_')
        ;

      return [
               "<script src='//cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.6/modernizr.min.js'><\/script>"
             , "<script> window.Modernizr || document.write('<script src=\"/js/rocket/vendors/modernizr-2.0.6.min.js\"><\\/script>')</script>"
             , (global.require_configuration ? "<script> var require = " + global.require_configuration + "</script>": "")
             //, (controller_name ? "<script> require.ready = function() { require(['" + controller_name + "' + '_client' ], function(m) { m.init.apply(m" + (args.length > 0 ? ", " + JSON.stringify(args) : '') + ");} ); }</script>": "")
             ].join('\n');
    };

    rocket_tmpl.footer  = function() {

      var args            = Array.prototype.slice.call(arguments)
        , controller_name = args.shift().replace(/[.,-;\s]+/g, '_')
        ;

      return [
                "<script>"
              , "  Modernizr.load(["
              , "      {"
              , "          load      : '/js/rocket/vendors/require-0.27.1.min.js'"
              , "        , callback  : function() {"
              , "             window.require || document.write('<script src=\"/js/rocket/vendors/require-0.27.1.min.js\"><\\/script>')"
              , ( typeof controller_name !== 'undefined' ? "             require(['" + controller_name + "' + '_client' ], function(m) { m.init.apply(m" + (args.length > 0 ? ", " + 'JSON.parse(' + JSON.stringify(args) + ')' : '') + ");} ); ": "")
              , "          }"
              , "      }"
              , "  ]);"
              , "</script>"

              ].join('\n')
     };

/******************************************************************************
 * Destroy all the routes associated with a given controller
 *
 * @param app {Object} The `app` object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controller_name {String} The name of the controller
 */
function destroyControllerRoutes(app, routes_dadt, controller_name) {

  var routes = routes_dadt.get(controller_name).get('routes')
    ;

  if (routes) {

    for (var route in routes.values) {

      app.remove(route);

    }

  }

  delete app.resources[controller_info.name];
  controller_info.set('routes', dadt.createNode());

}

/******************************************************************************
 * Builds a wrapper function sending raw json as a response if no view is found
 * or a rendered view
 *
 * @param controller_info {dadtNode} The dadtNode containing the controller
 *                                   info.
 *
 * @param method_func {Function} The method function.
 */
function buildWrapper(controller_info, method_func) {

  return function(req, res, next) {

    var view_file   = controller_info.get('view')

      , layout_node = controller_info.parent.get('layout')
      , layout      = (typeof layout_node !== 'undefined' ? layout_node.get('view') : true)

      , oSend       = res.send
      , oRender     = res.render
      ;

    //check if we have a view & if that we do not have an XHR-Request.
    //If the view is missing or if we have an XHR-Request, skip view rendering.
    if (!req.xhr && view_file) {

      //restore the original `res.send` function when `res.render` is called.
      res.render = function() {

        res.send = oSend;
        oRender.apply(this, arguments);

      };

      //replace the original send function to be able to automatically render
      //the wanted view.
      res.send = function(obj) {

        obj = obj || {};

        obj.layout = layout

        obj.controller = obj.controller || controller_info.parent.get('name');

        obj.rocket = obj.rocket || rocket_tmpl;

        res.send = oSend;

        res.render(view_file, obj);

      };

    }

    method_func(req, res, next);

  };

}

/******************************************************************************
 * Builds and set up the routes of the given controller. Removing the previous
 * routes if necessary.
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param controller_info {Object} The dadt node containing the controller info.
 *
 * @param controller {Object} The controller object to route.
 */
function buildRoutes(app, controller_info, controller) {

  var wrapped_funcs = {}
    , split         = []

    , resource
    , load
    , name = controller_info.get('name')
    ;

  for (var key in controller) {

    if (key.substr(0,1) === '_') {

      if (key === '_load' && typeof controller[key] === 'function') {

        //if the method name is `_load`  and it is a function ... save it.

        loader = controller[key];

      }

      //ignore keys beginning with a `_` unless it is `_load`
      continue;

    }

    controller_info.setIfNone(key, dadt.createNode());
    innerNode = controller_info.get(key);

    if (typeof controller[key] === 'object' && controller[key] !== null) {

      var verbs       = _.functions(controller[key])
        , route       = (name === 'root' ? path.join('/', key) : path.join('/', name,  key))
        , param       = ':' + lingo.en.singularize(name)
        , route_param = path.join(route, param)
        ;

      innerNode.set('name', [name, key].join('.'));

      for (var i = 0, ii = verbs.length; i < ii; i++) {

        var current_verb  = verbs[i]
          , method        = controller[key][current_verb]
          ;

        if (['get', 'post', 'put', 'del'].indexOf(current_verb) !== -1) {

          innerNode.setIfNone(current_verb, dadt.createNode());

          var wrapped           = undefined
            , current_verb_node = innerNode.get(current_verb)
            ;

          wrapped = buildWrapper(current_verb_node, method);

          current_verb_node.set('handler', wrapped);

          app[current_verb](route, wrapped);
          app[current_verb](route_param, wrapped);

          controller_info.get('routes').setIfNone(route, true);
          controller_info.get('routes').setIfNone(route_param, true);

        }

      }

    } else if(typeof controller[key] === 'function') {

      var wrapped     = undefined
        , key_node    = controller_info.get(key)
        , route       = (name === 'root' ? path.join('/', key) : path.join('/', name,  key))
        , param       = ':' + lingo.en.singularize(name)
        , route_param = path.join(route, param)
        ;

      wrapped = buildWrapper(innerNode, controller[key]);

      innerNode.set('handler', wrapped);

      //if the function is a custom controller method ...
      if (['index', 'show', 'new', 'create', 'edit', 'update', 'destroy'].indexOf(key) === -1) {

        app.all(route, wrapped);
        app.all(route_param, wrapped);

        controller_info.get('routes').setIfNone(route, true);
        controller_info.get('routes').setIfNone(route_param, true);

      } else {

        app.remove(route);
        app.remove(route_param);

        wrapped_funcs[key] = wrapped;

      }

    }

  }

  if(name === 'root') {

    resource = app.resource(wrapped_funcs);

  } else {

    resource = app.resource(name, wrapped_funcs);

  }

  if(typeof load !== 'undefined') {

    resource.load(load);

  }

  for (var verb in resource.routes) {

    for (var url in resource.routes[verb]) {

      controller_info.get('routes').setIfNone(url, true);

    }

  }

}

/******************************************************************************
 * Function called either when a new controller file is found or when a change
 * is detected. It takes care of clearing the `require` cache and creating the
 * controllers dadt node.
 *
 *
 * @param app {Object} The app object as returned by express, connect, or
 *                         rocket.
 *
 * @param controller_info {Object} The dadt node containing the controller info.
 *
 * @param controller_path {Object} The path of the current
 *                                 controller file.
 *
 * @param callback {Function} A callback of the form `function(err)`.
 *
 */
function loadController() {

  var args            = Array.prototype.slice.call(arguments)

    , app             = args.shift()

    , controller_info = args.shift()

    , controller_path = args.shift()

    , callback        = args.pop()

    , controller
    ;

  controller = require(controller_path);

  buildRoutes(app, controller_info, controller);

  if (callback) {
      callback(null);
  }
}

/******************************************************************************
 * Sets up the listeners on the `controllers` directory and calls
 * `loadController`.
 *
 * This function is also called each time a new controller file is found.
 *
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controller_file {Object} The FileEventEmitter object of the current
 * controller file.
 *
 * @param matches {Array} Array of the matched patterns of the RegExp selecting
 * this controller file.
 *
 *
 * @param callback {Function} A callback of the form `function(err)`
 *
 */
function setupController(app, routes_dadt, controller_file, matches, callback) {

  var controller_name = matches[1]

    , controller_path = controller_file.path

    , reload
    ;

  routes_dadt.setIfNone(controller_name, dadt.createNode());

  controller_info = routes_dadt.get(controller_name);

  controller_info.set('routes', dadt.createNode());

  controller_name = lingo.camelcase(controller_name.replace(/_/g, ' '));

  controller_info.set('name', controller_name);

  reload = async.apply(
    loadController
    , app
    , controller_info
    , controller_path
  )

  controller_file.on('change', function(curr, prev) {

    if (curr.nlink > 0) {

      if (require.cache[controller_path]) {

        delete require.cache[controller_path];

      }

      destroyControllerRoutes(app, routes_dadt, controller_name);

      reload();

    }

  });

  controller_file.on('destroy', function() {

    controller_file.unwatch();
    destroyControllerRoutes(app, routes_dadt, controller_name)

  });

  reload(callback);

}

/******************************************************************************
 * Setups the controllers, also watches the `controllers` directory for new
 * files and calls `setupController` accordingly.
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controllers {Object} The FileEventEmitter object of the `controllers`
 * folder.
 *
 * @param callback {Function} A callback of the form `function(err)`
 */
exports.setup = function setup_controller() {

  var args        = Array.prototype.slice.call(arguments)

    , app         = args.shift()
    , routes_dadt = args.shift()

    , controllers = args.shift()

    , callback    = args.pop()
    ;

  app.rocket_routes = routes_dadt;

  app.use(express.methodOverride());

  asyncFs.mapDir(

      controllers.path

    , [ CONTROLLER_NAME_REGEXP , async.apply(setupController, app, routes_dadt) ]

    , callback

  );

}<commit_msg>fixed missing quotes in passing boot args<commit_after>/******************************************************************************
 * Module dependencies
 */
var express             = require('express')
  , __                  = require('express-resource')
  , async               = require('async')
  , path                = require('path')
  , _                   = require('underscore')
  , lingo               = require('lingo')
  ;

//project related dependencies
var asyncFs             = require('./util/async_fs')
  , dadt                = require('./util/dadt')
  , config              = require('./config')

  , CONTROLLER_NAME_REGEXP   = config.CONTROLLER_NAME_REGEXP
  ;

/******************************************************************************
 * Rocket template name space
 */
var rocket_tmpl         = {};

    rocket_tmpl.header  = function() {

      var args            = Array.prototype.slice.call(arguments)
        , controller_name = args.shift().replace(/[.,-;\s]+/g, '_')
        ;

      return [
               "<script src='//cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.6/modernizr.min.js'><\/script>"
             , "<script> window.Modernizr || document.write('<script src=\"/js/rocket/vendors/modernizr-2.0.6.min.js\"><\\/script>')</script>"
             , (global.require_configuration ? "<script> var require = " + global.require_configuration + "</script>": "")
             //, (controller_name ? "<script> require.ready = function() { require(['" + controller_name + "' + '_client' ], function(m) { m.init.apply(m" + (args.length > 0 ? ", " + JSON.stringify(args) : '') + ");} ); }</script>": "")
             ].join('\n');
    };

    rocket_tmpl.footer  = function() {

      var args            = Array.prototype.slice.call(arguments)
        , controller_name = args.shift().replace(/[.,-;\s]+/g, '_')
        ;

      return [
                "<script>"
              , "  Modernizr.load(["
              , "      {"
              , "          load      : '/js/rocket/vendors/require-0.27.1.min.js'"
              , "        , callback  : function() {"
              , "             window.require || document.write('<script src=\"/js/rocket/vendors/require-0.27.1.min.js\"><\\/script>')"
              , ( typeof controller_name !== 'undefined' ? "             require(['" + controller_name + "' + '_client' ], function(m) { m.init.apply(m" + (args.length > 0 ? ", " + 'JSON.parse(\'' + JSON.stringify(args) + '\')' : '') + ");} ); ": "")
              , "          }"
              , "      }"
              , "  ]);"
              , "</script>"

              ].join('\n')
     };

/******************************************************************************
 * Destroy all the routes associated with a given controller
 *
 * @param app {Object} The `app` object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controller_name {String} The name of the controller
 */
function destroyControllerRoutes(app, routes_dadt, controller_name) {

  var routes = routes_dadt.get(controller_name).get('routes')
    ;

  if (routes) {

    for (var route in routes.values) {

      app.remove(route);

    }

  }

  delete app.resources[controller_info.name];
  controller_info.set('routes', dadt.createNode());

}

/******************************************************************************
 * Builds a wrapper function sending raw json as a response if no view is found
 * or a rendered view
 *
 * @param controller_info {dadtNode} The dadtNode containing the controller
 *                                   info.
 *
 * @param method_func {Function} The method function.
 */
function buildWrapper(controller_info, method_func) {

  return function(req, res, next) {

    var view_file   = controller_info.get('view')

      , layout_node = controller_info.parent.get('layout')
      , layout      = (typeof layout_node !== 'undefined' ? layout_node.get('view') : true)

      , oSend       = res.send
      , oRender     = res.render
      ;

    //check if we have a view & if that we do not have an XHR-Request.
    //If the view is missing or if we have an XHR-Request, skip view rendering.
    if (!req.xhr && view_file) {

      //restore the original `res.send` function when `res.render` is called.
      res.render = function() {

        res.send = oSend;
        oRender.apply(this, arguments);

      };

      //replace the original send function to be able to automatically render
      //the wanted view.
      res.send = function(obj) {

        obj = obj || {};

        obj.layout = layout

        obj.controller = obj.controller || controller_info.parent.get('name');

        obj.rocket = obj.rocket || rocket_tmpl;

        res.send = oSend;

        res.render(view_file, obj);

      };

    }

    method_func(req, res, next);

  };

}

/******************************************************************************
 * Builds and set up the routes of the given controller. Removing the previous
 * routes if necessary.
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param controller_info {Object} The dadt node containing the controller info.
 *
 * @param controller {Object} The controller object to route.
 */
function buildRoutes(app, controller_info, controller) {

  var wrapped_funcs = {}
    , split         = []

    , resource
    , load
    , name = controller_info.get('name')
    ;

  for (var key in controller) {

    if (key.substr(0,1) === '_') {

      if (key === '_load' && typeof controller[key] === 'function') {

        //if the method name is `_load`  and it is a function ... save it.

        loader = controller[key];

      }

      //ignore keys beginning with a `_` unless it is `_load`
      continue;

    }

    controller_info.setIfNone(key, dadt.createNode());
    innerNode = controller_info.get(key);

    if (typeof controller[key] === 'object' && controller[key] !== null) {

      var verbs       = _.functions(controller[key])
        , route       = (name === 'root' ? path.join('/', key) : path.join('/', name,  key))
        , param       = ':' + lingo.en.singularize(name)
        , route_param = path.join(route, param)
        ;

      innerNode.set('name', [name, key].join('.'));

      for (var i = 0, ii = verbs.length; i < ii; i++) {

        var current_verb  = verbs[i]
          , method        = controller[key][current_verb]
          ;

        if (['get', 'post', 'put', 'del'].indexOf(current_verb) !== -1) {

          innerNode.setIfNone(current_verb, dadt.createNode());

          var wrapped           = undefined
            , current_verb_node = innerNode.get(current_verb)
            ;

          wrapped = buildWrapper(current_verb_node, method);

          current_verb_node.set('handler', wrapped);

          app[current_verb](route, wrapped);
          app[current_verb](route_param, wrapped);

          controller_info.get('routes').setIfNone(route, true);
          controller_info.get('routes').setIfNone(route_param, true);

        }

      }

    } else if(typeof controller[key] === 'function') {

      var wrapped     = undefined
        , key_node    = controller_info.get(key)
        , route       = (name === 'root' ? path.join('/', key) : path.join('/', name,  key))
        , param       = ':' + lingo.en.singularize(name)
        , route_param = path.join(route, param)
        ;

      wrapped = buildWrapper(innerNode, controller[key]);

      innerNode.set('handler', wrapped);

      //if the function is a custom controller method ...
      if (['index', 'show', 'new', 'create', 'edit', 'update', 'destroy'].indexOf(key) === -1) {

        app.all(route, wrapped);
        app.all(route_param, wrapped);

        controller_info.get('routes').setIfNone(route, true);
        controller_info.get('routes').setIfNone(route_param, true);

      } else {

        app.remove(route);
        app.remove(route_param);

        wrapped_funcs[key] = wrapped;

      }

    }

  }

  if(name === 'root') {

    resource = app.resource(wrapped_funcs);

  } else {

    resource = app.resource(name, wrapped_funcs);

  }

  if(typeof load !== 'undefined') {

    resource.load(load);

  }

  for (var verb in resource.routes) {

    for (var url in resource.routes[verb]) {

      controller_info.get('routes').setIfNone(url, true);

    }

  }

}

/******************************************************************************
 * Function called either when a new controller file is found or when a change
 * is detected. It takes care of clearing the `require` cache and creating the
 * controllers dadt node.
 *
 *
 * @param app {Object} The app object as returned by express, connect, or
 *                         rocket.
 *
 * @param controller_info {Object} The dadt node containing the controller info.
 *
 * @param controller_path {Object} The path of the current
 *                                 controller file.
 *
 * @param callback {Function} A callback of the form `function(err)`.
 *
 */
function loadController() {

  var args            = Array.prototype.slice.call(arguments)

    , app             = args.shift()

    , controller_info = args.shift()

    , controller_path = args.shift()

    , callback        = args.pop()

    , controller
    ;

  controller = require(controller_path);

  buildRoutes(app, controller_info, controller);

  if (callback) {
      callback(null);
  }
}

/******************************************************************************
 * Sets up the listeners on the `controllers` directory and calls
 * `loadController`.
 *
 * This function is also called each time a new controller file is found.
 *
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controller_file {Object} The FileEventEmitter object of the current
 * controller file.
 *
 * @param matches {Array} Array of the matched patterns of the RegExp selecting
 * this controller file.
 *
 *
 * @param callback {Function} A callback of the form `function(err)`
 *
 */
function setupController(app, routes_dadt, controller_file, matches, callback) {

  var controller_name = matches[1]

    , controller_path = controller_file.path

    , reload
    ;

  routes_dadt.setIfNone(controller_name, dadt.createNode());

  controller_info = routes_dadt.get(controller_name);

  controller_info.set('routes', dadt.createNode());

  controller_name = lingo.camelcase(controller_name.replace(/_/g, ' '));

  controller_info.set('name', controller_name);

  reload = async.apply(
    loadController
    , app
    , controller_info
    , controller_path
  )

  controller_file.on('change', function(curr, prev) {

    if (curr.nlink > 0) {

      if (require.cache[controller_path]) {

        delete require.cache[controller_path];

      }

      destroyControllerRoutes(app, routes_dadt, controller_name);

      reload();

    }

  });

  controller_file.on('destroy', function() {

    controller_file.unwatch();
    destroyControllerRoutes(app, routes_dadt, controller_name)

  });

  reload(callback);

}

/******************************************************************************
 * Setups the controllers, also watches the `controllers` directory for new
 * files and calls `setupController` accordingly.
 *
 * @param app {Object} The app object as returned by express, connect, or
 * rocket.
 *
 * @param routes_dadt {Object} The root of routes' dadt data structure.
 *
 * @param controllers {Object} The FileEventEmitter object of the `controllers`
 * folder.
 *
 * @param callback {Function} A callback of the form `function(err)`
 */
exports.setup = function setup_controller() {

  var args        = Array.prototype.slice.call(arguments)

    , app         = args.shift()
    , routes_dadt = args.shift()

    , controllers = args.shift()

    , callback    = args.pop()
    ;

  app.rocket_routes = routes_dadt;

  app.use(express.methodOverride());

  asyncFs.mapDir(

      controllers.path

    , [ CONTROLLER_NAME_REGEXP , async.apply(setupController, app, routes_dadt) ]

    , callback

  );

}<|endoftext|>


<commit_before>$(document).ready(function(){
    var w = $('.wrap-image').width();
	$('.wrap-image').height(w);
	$('.preview-image').height(w);
});<commit_msg>post list js responding to window resize events<commit_after>img_resize = function(){
    var w = $('.wrap-image').width();
	$('.wrap-image').height(w);
	$('.preview-image').height(w);
};

$(window).resize(img_resize);
$(document).ready(img_resize);<|endoftext|>


<commit_before>/**
 * Dependencies
 */

var Logger = require('./logger');

var basename = require('path').basename;
var table = require('text-table');
var glob = require('glob').sync;
var util = require('./util');
var fs = require('fs');

var extend = util.extend;
var take = util.take;

/**
 * Expose `Program`
 */

module.exports = Program;

/**
 * Program
 */

function Program (options) {
	this.name = basename(process.argv[1]);
	this.path = process.cwd();
	this.delimiter = ' ';
	this.stdout = process.stdout;

	// if options is a string
	// assume it's a path
	if ('string' === typeof options) this.path = options;

	// if it's an object
	// extend this with it
	if ('object' === typeof options) extend(this, options);
}

/**
 * Program prototype
 */

var program = Program.prototype;

/**
 * Methods
 */

/**
 * Set option
 *
 * @param {String,Object} key
 * @param {Mixed} value - value
 * @returns {Mixed}
 * @api public
 */

program.set = function (key, value) {
	if ('object' === typeof key) {
		return extend(this, key);
	}

	return this[key] = value;
};

/**
 * Get option
 *
 * @param {String} key
 * @returns {Mixed}
 * @api public
 */

program.get = function (key) {
	return this[key] = value;
};

/**
 * Setup commands
 *
 * @api private
 */

program.setupCommands = function () {
	var rootPath = this.path;
	var path = rootPath + '/commands';

	if (!fs.existsSync(path)) return;

	var program = this;
	var delimiter = this.delimiter;

	// search through ./commands folder
	var commands = glob(path + '/**/*');

	// level of nesting for commands
	var level = 0;

	// filter out non-js files
	commands = commands.filter(function (file) {
		return /\.js$/.test(file);
	});

	path = path.replace(/\\/g, '/');

	// strip root path and apply delimiter
	commands = commands.map(function (file) {
	  // remove root path from file path
	  // remove .js extension
	  // replace slashes with a delimiter
		file = file.replace(/\\/g, '/')
					      .replace(path + '/', '')
					      .replace('.js', '')
					      .replace(/\//g, delimiter);

		// find number of delimiters
		// in command name
		var match = file.match(new RegExp(delimiter));
		var delimiters = (match || []).length;

		// if delimiter count is more than
		// previous level
		// increase it
		if (delimiters > level) level = delimiters;

		return file;
	});

	// sort files by path length
	commands.sort(function (a, b) {
		return a.length > b.length ? 1 : -1;
	});

	// assign handlers
	commands.forEach(function (name) {
	  // path to command
		var path = rootPath + '/commands/' + name.replace(new RegExp(delimiter), '/');

		// command class
		var command = require(path);

		// extend command with its name, program and path
		command.prototype.program = program;
		command.prototype.path = path;
		command.prototype.name = name;

		// set nesting level for command
		var match = name.match(new RegExp(delimiter));
		command.prototype.level = (match || []).length;

		// register command
		commands[name] = command;
	});

	this.commands = commands;
	this.level = level;
}

/**
 * Run a program
 *
 * @api public
 */

program.run = function () {
  // search through ./commands folder
  // and register all commands
	this.setupCommands();

	// strip "node" and path to a script
	var args = process.argv.slice(2);

	// no options, print program help
	if (!args.length) {
		return this.help();
	}

	// if there is "-h" or "--help"
	// this should be a help command
	var isHelp = args.includes('-h') || args.includes('--help');

	// if there is ONLY help option
	// this is a root help command
	if (1 === args.length && isHelp) {
		return this.help();
	}

	var level = this.level;
	var commands = this.commands;

	while (level >= 0) {
	  // get n first items of program arguments
		var name = take(args, level + 1).join(this.delimiter);

		if (commands[name]) {
			// remove command name from process.argv
			process.argv.splice(0, level + 3);

			if (isHelp) {
				this.helpCommand(name);
			} else {
				this.runCommand(name);
			}

			return;
		}

		level--;
	}

	Logger.log('error', 'Command does not exist.');
};

/**
 * Run specific command
 *
 * @param {String} name
 * @api private
 */

program.runCommand = function (name) {
	// get handler for command
	var command = new this.commands[name];

	try {
		command.execute();
	} catch (err) {
		var nativeErrors = [
			'EvalError',
			'InternalError',
			'RangeError',
			'ReferenceError',
			'SyntaxError',
			'TypeError',
			'URIError'
		];

		// if the error is related to code
		// throw it
		if (nativeErrors.includes(err.name)) throw err;

		// if not, assume that it was thrown on purpose
		Logger.log('error', err.message);
	}
};

/**
 * Print program help
 *
 * @api private
 */

program.help = function () {
	this.stdout.write('Usage: ' + this.name + ' COMMAND [OPTIONS]\n\n');

	if (this.desc) {
		this.stdout.write('  ' + this.desc + '\n\n');
	}

	// get top-level commands and
	// show compact help for each of them
	var commands = this.commandsForLevel(0);
	if (!commands.length) return;

	var output = [];

	commands.forEach(function (command) {
		command = new command();
		var help = command.help('compact');

		output.push([command.name, help]);
	});

	this.stdout.write('Available commands:\n\n');

	// build output
	output = table(output);

	// write generated output
	this.stdout.write(output + '\n');
};

/**
 * Print help for a specific command
 *
 * @param {String} name
 * @api private
 */

program.helpCommand = function (name) {
	// get handler for command
	var command = new this.commands[name];

	this.stdout.write(command.help());

	// if there are sub-commands
	// (commands on the deeper level)
	// show compact help for each of them
	var subcommands = this.commandsForLevel(command.level + 1);
	subcommands = subcommands.filter(function (subcommand) {
		return subcommand.prototype.name.indexOf(command.name) === 0;
	});

	if (subcommands.length) {
		this.stdout.write('Additional commands:\n\n');

		var output = [];

		subcommands.forEach(function (subcommand) {
			subcommand = new subcommand();
			var help = subcommand.help('compact');

			output.push([subcommand.name, help]);
		});

		// build output
		output = table(output);

		// write generated output
		this.stdout.write(output + '\n');
	}
};

/**
 * Get a list of commands
 * For a specific level/floor
 *
 * @param {Number} level
 * @returns {Array}
 * @api private
 */

program.commandsForLevel = function (level) {
	var delimiter = this.delimiter;
	var commands = this.commands;

	return commands.filter(function (name) {
		var command = commands[name].prototype;

		// check if there is an index command
		// apps/create.js => apps.js
		var path = command.path.split('/').slice(0, -1).join('/') + '.js';

		if (!fs.existsSync(path)) {
		  if (command.level > 0) command.level--;
		}

		return command.level === level;
	}).map(function (command) {
		return commands[command];
	});
};
<commit_msg>fixed typo that breaks program.get<commit_after>/**
 * Dependencies
 */

var Logger = require('./logger');

var basename = require('path').basename;
var table = require('text-table');
var glob = require('glob').sync;
var util = require('./util');
var fs = require('fs');

var extend = util.extend;
var take = util.take;

/**
 * Expose `Program`
 */

module.exports = Program;

/**
 * Program
 */

function Program (options) {
	this.name = basename(process.argv[1]);
	this.path = process.cwd();
	this.delimiter = ' ';
	this.stdout = process.stdout;

	// if options is a string
	// assume it's a path
	if ('string' === typeof options) this.path = options;

	// if it's an object
	// extend this with it
	if ('object' === typeof options) extend(this, options);
}

/**
 * Program prototype
 */

var program = Program.prototype;

/**
 * Methods
 */

/**
 * Set option
 *
 * @param {String,Object} key
 * @param {Mixed} value - value
 * @returns {Mixed}
 * @api public
 */

program.set = function (key, value) {
	if ('object' === typeof key) {
		return extend(this, key);
	}

	return this[key] = value;
};

/**
 * Get option
 *
 * @param {String} key
 * @returns {Mixed}
 * @api public
 */

program.get = function (key) {
	return this[key];
};

/**
 * Setup commands
 *
 * @api private
 */

program.setupCommands = function () {
	var rootPath = this.path;
	var path = rootPath + '/commands';

	if (!fs.existsSync(path)) return;

	var program = this;
	var delimiter = this.delimiter;

	// search through ./commands folder
	var commands = glob(path + '/**/*');

	// level of nesting for commands
	var level = 0;

	// filter out non-js files
	commands = commands.filter(function (file) {
		return /\.js$/.test(file);
	});

	path = path.replace(/\\/g, '/');

	// strip root path and apply delimiter
	commands = commands.map(function (file) {
	  // remove root path from file path
	  // remove .js extension
	  // replace slashes with a delimiter
		file = file.replace(/\\/g, '/')
					      .replace(path + '/', '')
					      .replace('.js', '')
					      .replace(/\//g, delimiter);

		// find number of delimiters
		// in command name
		var match = file.match(new RegExp(delimiter));
		var delimiters = (match || []).length;

		// if delimiter count is more than
		// previous level
		// increase it
		if (delimiters > level) level = delimiters;

		return file;
	});

	// sort files by path length
	commands.sort(function (a, b) {
		return a.length > b.length ? 1 : -1;
	});

	// assign handlers
	commands.forEach(function (name) {
	  // path to command
		var path = rootPath + '/commands/' + name.replace(new RegExp(delimiter), '/');

		// command class
		var command = require(path);

		// extend command with its name, program and path
		command.prototype.program = program;
		command.prototype.path = path;
		command.prototype.name = name;

		// set nesting level for command
		var match = name.match(new RegExp(delimiter));
		command.prototype.level = (match || []).length;

		// register command
		commands[name] = command;
	});

	this.commands = commands;
	this.level = level;
}

/**
 * Run a program
 *
 * @api public
 */

program.run = function () {
  // search through ./commands folder
  // and register all commands
	this.setupCommands();

	// strip "node" and path to a script
	var args = process.argv.slice(2);

	// no options, print program help
	if (!args.length) {
		return this.help();
	}

	// if there is "-h" or "--help"
	// this should be a help command
	var isHelp = args.includes('-h') || args.includes('--help');

	// if there is ONLY help option
	// this is a root help command
	if (1 === args.length && isHelp) {
		return this.help();
	}

	var level = this.level;
	var commands = this.commands;

	while (level >= 0) {
	  // get n first items of program arguments
		var name = take(args, level + 1).join(this.delimiter);

		if (commands[name]) {
			// remove command name from process.argv
			process.argv.splice(0, level + 3);

			if (isHelp) {
				this.helpCommand(name);
			} else {
				this.runCommand(name);
			}

			return;
		}

		level--;
	}

	Logger.log('error', 'Command does not exist.');
};

/**
 * Run specific command
 *
 * @param {String} name
 * @api private
 */

program.runCommand = function (name) {
	// get handler for command
	var command = new this.commands[name];

	try {
		command.execute();
	} catch (err) {
		var nativeErrors = [
			'EvalError',
			'InternalError',
			'RangeError',
			'ReferenceError',
			'SyntaxError',
			'TypeError',
			'URIError'
		];

		// if the error is related to code
		// throw it
		if (nativeErrors.includes(err.name)) throw err;

		// if not, assume that it was thrown on purpose
		Logger.log('error', err.message);
	}
};

/**
 * Print program help
 *
 * @api private
 */

program.help = function () {
	this.stdout.write('Usage: ' + this.name + ' COMMAND [OPTIONS]\n\n');

	if (this.desc) {
		this.stdout.write('  ' + this.desc + '\n\n');
	}

	// get top-level commands and
	// show compact help for each of them
	var commands = this.commandsForLevel(0);
	if (!commands.length) return;

	var output = [];

	commands.forEach(function (command) {
		command = new command();
		var help = command.help('compact');

		output.push([command.name, help]);
	});

	this.stdout.write('Available commands:\n\n');

	// build output
	output = table(output);

	// write generated output
	this.stdout.write(output + '\n');
};

/**
 * Print help for a specific command
 *
 * @param {String} name
 * @api private
 */

program.helpCommand = function (name) {
	// get handler for command
	var command = new this.commands[name];

	this.stdout.write(command.help());

	// if there are sub-commands
	// (commands on the deeper level)
	// show compact help for each of them
	var subcommands = this.commandsForLevel(command.level + 1);
	subcommands = subcommands.filter(function (subcommand) {
		return subcommand.prototype.name.indexOf(command.name) === 0;
	});

	if (subcommands.length) {
		this.stdout.write('Additional commands:\n\n');

		var output = [];

		subcommands.forEach(function (subcommand) {
			subcommand = new subcommand();
			var help = subcommand.help('compact');

			output.push([subcommand.name, help]);
		});

		// build output
		output = table(output);

		// write generated output
		this.stdout.write(output + '\n');
	}
};

/**
 * Get a list of commands
 * For a specific level/floor
 *
 * @param {Number} level
 * @returns {Array}
 * @api private
 */

program.commandsForLevel = function (level) {
	var delimiter = this.delimiter;
	var commands = this.commands;

	return commands.filter(function (name) {
		var command = commands[name].prototype;

		// check if there is an index command
		// apps/create.js => apps.js
		var path = command.path.split('/').slice(0, -1).join('/') + '.js';

		if (!fs.existsSync(path)) {
		  if (command.level > 0) command.level--;
		}

		return command.level === level;
	}).map(function (command) {
		return commands[command];
	});
};
<|endoftext|>


<commit_before>var Promise = require("bluebird/js/main/promise")();

module.exports = function (pa) {

  var phantasma = pa;

  // methods to run after previous promise has resolved
  [
    'open',
    'exit',
    'wait',
    'viewport',
    'screenshot',
    'evaluate',
    'type',
    'value',
    'select',
    'click',
    'title',
    'url',
    'forward',
    'back',
    'refresh',
    'focus',
    'injectJs',
    'injectCss',
    'content',
    'pageSetting'
  ].forEach(function (method) {
    Promise.prototype[method] = function () {
      var self = this;
      var args = [].slice.call(arguments);
      return this.then(function () {
        return phantasma[method].apply(phantasma, args);
      });
    };
  });

  // methods to run immediately
  ['on', 'once'].forEach(function (method) {
    Promise.prototype[method] = function () {
      var args = [].slice.call(arguments);
      return phantasma[method].apply(phantasma, args);
    };
  });

  return Promise;

};
<commit_msg>Added extractDomElement promise<commit_after>var Promise = require("bluebird/js/main/promise")();

module.exports = function (pa) {

  var phantasma = pa;

  // methods to run after previous promise has resolved
  [
    'open',
    'exit',
    'wait',
    'viewport',
    'screenshot',
    'evaluate',
    'type',
    'value',
    'select',
    'click',
    'title',
    'url',
    'forward',
    'back',
    'refresh',
    'focus',
    'injectJs',
    'injectCss',
    'content',
    'pageSetting',
    'extractDomElement'
  ].forEach(function (method) {
    Promise.prototype[method] = function () {
      var self = this;
      var args = [].slice.call(arguments);
      return this.then(function () {
        return phantasma[method].apply(phantasma, args);
      });
    };
  });

  // methods to run immediately
  ['on', 'once'].forEach(function (method) {
    Promise.prototype[method] = function () {
      var args = [].slice.call(arguments);
      return phantasma[method].apply(phantasma, args);
    };
  });

  return Promise;

};
<|endoftext|>


<commit_before>'use strict';
const colorList = require('./public/json/colors.json');
const dotenv = require('dotenv');
const _ = require('lodash');
const { error } = dotenv.config({ silent: true });
const axios = require('axios');
const error_messages = require('./error_messages');

// load environment file
if (error) {
    console.warn(error.message);
}

const client_id = process.env.CLIENT_ID || false;
const client_secret = process.env.CLIENT_SECRET || false;
if (!(client_id && client_secret)) {
    throw error_messages.CLIENT_ID_AND_CLIENT_SECRET_MISSING;
}

const getEmoji = (repoLength) => {
    if (repoLength >= 100) {
        return '💯 👍 😎 👏';
    } else if (repoLength >= 75) {
        return '👍 😎 👏';
    } else if (repoLength >= 50) {
        return '👍 😎';
    } else if (repoLength >= 20) {
        return '👍';
    } else if (repoLength > 0) {
        return '🙂';
    } else if (repoLength === 0) {
        return '😪';
    }

    return '';
};

const getColors = (languages) => {
    const defaultColor = '#000';
    const colors = [];
    Object.keys(languages).forEach((key) => {
        colors.push(colorList[key] || defaultColor);
    });
    return colors;
};

const reqUser = (username) => {
    const url = `https://api.github.com/users/${username}?client_id=${client_id}&client_secret=${client_secret}`;
    const headers = { 'User-Agent': `${username}` };
    return axios.get(url, { headers });
};

const reqRepos = (username, numberOfPages) => {
    const headers = { 'User-Agent': `${username}` };
    const url = (page) =>
        `https://api.github.com/users/${username}/repos?client_id=${client_id}&client_secret=${client_secret}&per_page=100&page=${page}`;
    const requests = [];
    for (let i = 1; i <= numberOfPages; i++) {
        requests.push(axios.get(url(i), { headers }));
    }
    return requests;
};

exports.index = (req, res) => {
    const { username } = req.query;
    const currentYear = new Date().getFullYear();
    let avatar,
        msg,
        repos,
        statement,
        type,
        title = '';
    let languages = {};
    const statements = [
        'You\'ve got skills on',
        'Wow! You really like',
        'Good job on',
        'Keep going on'
    ];
    const mysteryStatements = [
        'Wow, you like the mystery language',
        'Wow, you are a mystery man!',
        'You must be like forking someone`s repo!'
    ];
    if (username === undefined) {
        res.render('layouts/main', {
            show: {
                result: false,
                chart: false
            },
            username: '',
            type,
            avatar,
            languages,
            msg,
            repos,
            statement,
            currentYear,
            title
        });
        return;
    }
    reqUser(username)
        .then((usr) => {
            const usrData = usr.data;
            avatar = usrData.avatar_url;
            type =
                usrData.type === 'Organization' ? 'Organization' : 'Personal';
            title = `| ${username} - ${type}`;
            const numberOfRepos = usrData.public_repos;
            if (numberOfRepos === 0) {
                repos = `${username} has no repo`;
                res.render('layouts/main', {
                    show: {
                        result: true,
                        chart: false
                    },
                    username,
                    type,
                    avatar,
                    languages: {},
                    msg,
                    repos,
                    showEmoji: getEmoji(numberOfRepos),
                    statement,
                    currentYear,
                    title
                });
                return;
            }
            const numberOfPages = parseInt(numberOfRepos / 100) + 1;
            axios.all(reqRepos(username, numberOfPages)).then((pages) => {
                const reposData = _.flatMap(pages, (page) => page.data);

                numberOfRepos === 1
                    ? (repos = `${numberOfRepos} repo`)
                    : (repos = `${numberOfRepos} repos`);
                reposData.forEach((repo) => {
                    languages[repo.language] =
                        (languages[repo.language] || 0) + 1;
                });
                languages = _.fromPairs(
                    _.sortBy(_.toPairs(languages), (a) => a[1]).reverse()
                );

                if (languages.null) {
                    languages.unknown = languages.null;
                    delete languages.null;
                }
                const goodAt = _.maxBy(_.keys(languages), (o) => languages[o]);
                const goodAtMysteryLanguage = goodAt === 'unknown';

                // change language of 'unknown' to 'unknown (fork repository)'
                const oldKey = 'unknown';
                const newKey = 'Unknown (forked repository)';
                const isContainUnknown =
                    Object.keys(languages).indexOf(oldKey) > 0 ? true : false;
                if (isContainUnknown) {
                    delete Object.assign(languages, {
                        [newKey]: languages[oldKey]
                    })[oldKey];
                }

                const randomStatement = goodAtMysteryLanguage
                    ? _.sample(mysteryStatements)
                    : _.sample(statements);
                goodAtMysteryLanguage
                    ? (statement = `${randomStatement}`)
                    : (statement = `${randomStatement} ${goodAt}!`);

                const limitLabel = Object.keys(languages).length > 20;

                res.render('layouts/main', {
                    show: {
                        result: true,
                        chart: true
                    },
                    username,
                    type,
                    avatar,
                    languages: JSON.stringify(languages),
                    colors: JSON.stringify(getColors(languages)),
                    showEmoji: getEmoji(numberOfRepos),
                    msg,
                    repos,
                    statement,
                    limitLabel,
                    goodAt,
                    currentYear,
                    title
                });
            });
        })
        .catch((err) => {
            if (err.response.data.message === 'Not Found') {
                msg = 'User was not found';
                res.render('layouts/main', {
                    show: {
                        result: false,
                        chart: false
                    },
                    username,
                    type,
                    avatar,
                    languages: JSON.stringify(languages),
                    colors: JSON.stringify(getColors(languages)),
                    msg,
                    repos,
                    statement,
                    currentYear,
                    title
                });
                return;
            }
        });
};

exports.notFound = (req, res) =>
    res.status(404).render('layouts/main', {
        error: {
            code: 404,
            message: 'Page not found!'
        }
    });
<commit_msg>revert controller.js<commit_after>'use strict';
const colorList = require('./public/json/colors.json');
const dotenv = require('dotenv');
const _ = require('lodash');
const { error } = dotenv.config({ silent: true });
const axios = require('axios');

// load environment file
if (error) {
    console.warn(error.message);
}

const client_id = process.env.CLIENT_ID || false;
const client_secret = process.env.CLIENT_SECRET || false;
if (!(client_id && client_secret)) {
    throw 'Missing CLIENT_ID and CLIENT_SECRET environment variables';
}

const getEmoji = (repoLength) => {
    if (repoLength >= 100) {
        return '💯 👍 😎 👏';
    } else if (repoLength >= 75) {
        return '👍 😎 👏';
    } else if (repoLength >= 50) {
        return '👍 😎';
    } else if (repoLength >= 20) {
        return '👍';
    } else if (repoLength > 0) {
        return '🙂';
    } else if (repoLength === 0) {
        return '😪';
    }

    return '';
};

const getColors = (languages) => {
    const defaultColor = '#000';
    const colors = [];
    Object.keys(languages).forEach((key) => {
        colors.push(colorList[key] || defaultColor);
    });
    return colors;
};

const reqUser = (username) => {
    const url = `https://api.github.com/users/${username}?client_id=${client_id}&client_secret=${client_secret}`;
    const headers = { 'User-Agent': `${username}` };
    return axios.get(url, { headers });
};

const reqRepos = (username, numberOfPages) => {
    const headers = { 'User-Agent': `${username}` };
    const url = (page) =>
        `https://api.github.com/users/${username}/repos?client_id=${client_id}&client_secret=${client_secret}&per_page=100&page=${page}`;
    const requests = [];
    for (let i = 1; i <= numberOfPages; i++) {
        requests.push(axios.get(url(i), { headers }));
    }
    return requests;
};

exports.index = (req, res) => {
    const { username } = req.query;
    const currentYear = new Date().getFullYear();
    let avatar, msg, repos, statement, type, title = '';
    let languages = {};
    const statements = [
        'You\'ve got skills on',
        'Wow! You really like',
        'Good job on',
        'Keep going on'
    ];
    const mysteryStatements = [
        'Wow, you like the mystery language',
        'Wow, you are a mystery man!',
        'You must be like forking someone`s repo!'
    ];
    if (username === undefined) {
        res.render('layouts/main', {
            show: {
                result: false,
                chart: false
            },
            username: '',
            type,
            avatar,
            languages,
            msg,
            repos,
            statement,
            currentYear,
            title
        });
        return;
    }
    reqUser(username)
        .then((usr) => {
            const usrData = usr.data;
            avatar = usrData.avatar_url;
            type = usrData.type === 'Organization' ? 'Organization' : 'Personal';
            title = `| ${username} - ${type}`;
            const numberOfRepos = usrData.public_repos;
            if (numberOfRepos === 0) {
                repos = `${username} has no repo`;
                res.render('layouts/main', {
                    show: {
                        result: true,
                        chart: false
                    },
                    username,
                    type,
                    avatar,
                    languages: {},
                    msg,
                    repos,
                    showEmoji: getEmoji(numberOfRepos),
                    statement,
                    currentYear,
                    title
                });
                return;
            }
            const numberOfPages = parseInt(numberOfRepos / 100) + 1;
            axios.all(reqRepos(username, numberOfPages))
                .then((pages) => {
                    const reposData = _.flatMap(pages, (page) => page.data);

                    (numberOfRepos === 1) ? repos = `${numberOfRepos} repo` : repos = `${numberOfRepos} repos`;
                    reposData.forEach((repo) => {
                        languages[repo.language] = (languages[repo.language] || 0) + 1;
                    });
                    languages = _.fromPairs(_.sortBy(_.toPairs(languages), (a) => a[1]).reverse());

                    if (languages.null) {
                        languages.unknown = languages.null;
                        delete languages.null;
                    }
                    const goodAt = _.maxBy(_.keys(languages), (o) => languages[o]);
                    const goodAtMysteryLanguage = (goodAt === 'unknown');

                    // change language of 'unknown' to 'unknown (fork repository)'
                    const oldKey = 'unknown';
                    const newKey = 'Unknown (forked repository)';
                    const isContainUnknown = Object.keys(languages).indexOf(oldKey) > 0 ? true : false;
                    if (isContainUnknown) {
                        delete Object.assign(languages, { [newKey] : languages[oldKey] })[oldKey];
                    }

                    const randomStatement = goodAtMysteryLanguage ? _.sample(mysteryStatements) : _.sample(statements);
                    goodAtMysteryLanguage ? statement = `${randomStatement}` : statement = `${randomStatement} ${goodAt}!`;

                    const limitLabel = Object.keys(languages).length > 20;

                    res.render('layouts/main', {
                        show: {
                            result: true,
                            chart: true
                        },
                        username,
                        type,
                        avatar,
                        languages: JSON.stringify(languages),
                        colors: JSON.stringify(getColors(languages)),
                        showEmoji: getEmoji(numberOfRepos),
                        msg,
                        repos,
                        statement,
                        limitLabel,
                        goodAt,
                        currentYear,
                        title
                    });
                });
        })
        .catch((err) => {
            if (err.response.data.message === 'Not Found') {
                msg = 'User was not found';
                res.render('layouts/main', {
                    show: {
                        result: false,
                        chart: false
                    },
                    username,
                    type,
                    avatar,
                    languages: JSON.stringify(languages),
                    colors: JSON.stringify(getColors(languages)),
                    msg,
                    repos,
                    statement,
                    currentYear,
                    title
                });
                return;
            }
        });
};

exports.notFound = (req, res) => res
    .status(404)
    .render('layouts/main', {
        error: {
            code: 404,
            message: 'Page not found!'
        }
    });
<|endoftext|>