<commit_before>const Base = require('db-migrate-pg').base;
const util = require('util');
let pg = require('pg');
const Promise = require('bluebird');

function dummy () {
  arguments[arguments.length - 1]('not implemented');
}

var CockroachDriver = Base.extend({
  init: function (connection, schema, intern) {
    this._super(connection, schema, intern);
  },

  addForeignKey: function (
    tableName,
    referencedTableName,
    keyName,
    fieldMapping,
    rules,
    callback
  ) {
    if (arguments.length === 5 && typeof rules === 'function') {
      callback = rules;
      rules = {};
    }
    var columns = Object.keys(fieldMapping);
    var referencedColumns = columns.map(function (key) {
      return '"' + fieldMapping[key] + '"';
    });
    var sql = util.format(
      'ALTER TABLE "%s" ADD CONSTRAINT "%s" FOREIGN KEY (%s) REFERENCES "%s" (%s) ON DELETE %s ON UPDATE %s',
      tableName,
      keyName,
      this.quoteDDLArr(columns).join(', '),
      referencedTableName,
      referencedColumns.join(', '),
      rules.onDelete || 'NO ACTION',
      rules.onUpdate || 'NO ACTION'
    );
    return this.runSql(sql).nodeify(callback);
  },

  _translateSpecialDefaultValues: function (
    spec,
    options,
    tableName,
    columnName
  ) {
    switch (spec.defaultValue.special) {
      case 'CURRENT_TIMESTAMP':
        spec.defaultValue.prep = 'CURRENT_TIMESTAMP()';
        break;
      default:
        this.super(spec, options, tableName, columnName);
        break;
    }
  },

  _handleMultiPrimaryKeys: function (primaryKeyColumns) {
    return util.format(
      ', PRIMARY KEY (%s)',
      this.quoteDDLArr(
        primaryKeyColumns
          .sort(function (a, b) {
            if (a.spec.interleave && b.spec.interleave) return 0;

            return a.spec.interleave ? -1 : 1;
          })
          .map(function (value) {
            return value.name;
          })
      ).join(', ')
    );
  },

  _applyTableOptions: function (options) {
    const columns = options.columns || options;
    const interleaves = [];
    const self = this;
    let sql = '';
    let interleave;

    Object.keys(columns).forEach(function (key) {
      var option = columns[key];

      if (option.interleave) {
        if (typeof option.interleave === 'string') {
          if (interleave && interleave !== option.interleave) {
            this.log.warn(
              'Ignoring interleave "' + interleave + '", you can only have one!'
            );
          } else {
            interleave = option.interleave;
          }

          interleaves.push(key);
        }
      }
    });

    if (interleaves.length > 0) {
      sql = util.format(
        ' INTERLEAVE IN PARENT %s (%s)',
        self.escapeDDL(interleave),
        self.quoteDDLArr(interleaves).join(', ')
      );
    }

    return sql;
  },

  _applyExtensions: function (options) {
    const columns = options.columns || options;
    const families = {};
    const indizies = {};
    const sql = [];
    const self = this;
    let firstFamily;

    Object.keys(columns).forEach(function (key) {
      var option = columns[key];

      if (option.family && typeof option.family === 'string') {
        families[option.family] = families[option.family] || [];
        families[option.family].push(self.escapeDDL(key));

        if (option.primaryKey === true) firstFamily = option.family;
      }

      if (option.foreignKey && typeof option.foreignKey === 'object') {
        indizies[option.foreignKey.name] =
          indizies[option.foreignKey.name] || [];
        indizies[option.foreignKey.name].push(self.escapeDDL(key));
      }
    });

    Object.keys(indizies).forEach(function (key) {
      sql.push(
        util.format(
          'INDEX %s (%s)',
          self.escapeDDL(key),
          indizies[key].join(', ')
        )
      );
    });

    if (firstFamily) {
      sql.push(
        util.format(
          'FAMILY %s (%s)',
          self.escapeDDL(firstFamily),
          families[firstFamily].join(', ')
        )
      );
    }

    Object.keys(families).forEach(function (key) {
      if (key !== firstFamily) {
        sql.push(
          util.format(
            'FAMILY %s (%s)',
            self.escapeDDL(key),
            families[key].join(', ')
          )
        );
      }
    });

    if (sql.length === 0) return '';

    return ', ' + sql.join(', ');
  },

  changeColumn: function (tableName, columnName, columnSpec, callback) {
    let options = {};
    if (typeof callback === 'object') {
      options = callback;
      callback = null;
    }

    return setNotNull.call(this).nodeify(callback);

    function setNotNull () {
      if (columnSpec.notNull === undefined) {
        return setUnique.call(this);
      }
      var setOrDrop = columnSpec.notNull === true ? 'SET' : 'DROP';
      var sql = util.format(
        'ALTER TABLE "%s" ALTER COLUMN "%s" %s NOT NULL',
        tableName,
        columnName,
        setOrDrop
      );

      return this.runSql(sql).then(setUnique.bind(this));
    }

    function setUnique () {
      var sql;
      var constraintName = tableName + '_' + columnName + '_key';

      if (columnSpec.unique === true) {
        sql = util.format(
          'ALTER TABLE "%s" ADD CONSTRAINT "%s" UNIQUE ("%s")',
          tableName,
          constraintName,
          columnName
        );
        return this.runSql(sql).then(setDefaultValue.bind(this));
      } else if (columnSpec.unique === false) {
        sql = util.format(
          'DROP INDEX "%s"@"%s" CASCADE',
          tableName,
          constraintName
        );
        return this.runSql(sql).then(setDefaultValue.bind(this));
      } else {
        return setDefaultValue.call(this);
      }
    }

    function setDefaultValue () {
      var sql;

      if (columnSpec.defaultValue !== undefined) {
        var defaultValue = null;
        if (typeof columnSpec.defaultValue === 'string') {
          defaultValue = "'" + columnSpec.defaultValue + "'";
        } else {
          defaultValue = columnSpec.defaultValue;
        }
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" SET DEFAULT %s',
          tableName,
          columnName,
          defaultValue
        );
      } else {
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" DROP DEFAULT',
          tableName,
          columnName
        );
      }
      return this.runSql(sql).then(setType.bind(this));
    }

    async function setType () {
      // no changes are possible afterwards in cockroachdb currently
      let sql = '';
      if (columnSpec.type !== undefined) {
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s%s',
          tableName,
          columnName,
          columnSpec.type,
          columnSpec.length ? `(${columnSpec.length})` : ''
        );
      }

      if (sql === '') {
        return Promise.resolve();
      } else {
        return this.runSql(sql);
      }
    }
  },

  mapDataType: function (str) {
    str = str.toLowerCase();
    switch (str) {
      case 'float':
      case 'timestamptz':
      case 'uuid':
      case 'jsonb':
        return str;
      case 'computed':
        return '';
    }
    return this._super(str);
  },

  createColumnConstraint: function (spec, options, tableName, columnName) {
    var constraint = [];
    var callbacks = [];
    var cb;
    let type = spec.type;

    // this must keep first as it wilkl replace against mapDataType
    if (type.toLowerCase() === 'computed') {
      type = `${this.mapDataType(spec.computedType)} AS (${spec.function})`;
      if (spec.stored) {
        type += ' STORED';
      }
      constraint.push(type);
    }

    if (spec.primaryKey) {
      if (spec.autoIncrement) {
        if (this.mapDataType(spec.type) === 'uuid') {
          constraint.push('UUID');
          spec.defaultValue = { prep: 'gen_random_uuid()' };
        } else {
          constraint.push('SERIAL');
        }
      }

      if (options.emitPrimaryKey) {
        constraint.push('PRIMARY KEY');
      }
    }

    if (spec.timezone) {
      constraint.push('WITH TIME ZONE');
    }

    if (spec.notNull === true) {
      constraint.push('NOT NULL');
    }

    if (spec.unique) {
      constraint.push('UNIQUE');
    }

    if (spec.defaultValue !== undefined) {
      constraint.push('DEFAULT');
      if (typeof spec.defaultValue === 'string') {
        constraint.push("'" + spec.defaultValue + "'");
      } else if (spec.defaultValue.raw) {
        constraint.push(spec.defaultValue.raw);
      } else if (spec.defaultValue.prep) {
        constraint.push(spec.defaultValue.prep);
      } else {
        constraint.push(spec.defaultValue);
      }
    }

    // keep foreignKey for backward compatiable, push to callbacks in the future
    if (spec.foreignKey) {
      cb = this.bindForeignKey(tableName, columnName, spec.foreignKey);
    }
    if (spec.comment) {
      // TODO: create a new function addComment is not callable from here
      callbacks.push(
        function (tableName, columnName, comment, callback) {
          var sql = util.format(
            "COMMENT on COLUMN %s.%s IS '%s'",
            tableName,
            columnName,
            comment
          );
          return this.runSql(sql).nodeify(callback);
        }.bind(this, tableName, columnName, spec.comment)
      );
    }

    return {
      foreignKey: cb,
      callbacks: callbacks,
      constraints: constraint.join(' ')
    };
  },

  removeIndex: function (tableName, indexName, callback) {
    var sql;
    if (arguments.length === 2 && typeof indexName === 'function') {
      callback = indexName;
      indexName = tableName;
      tableName = null;
    } else if (arguments.length === 1 && typeof tableName === 'string') {
      indexName = tableName;
      tableName = null;
    }

    if (tableName) {
      sql = util.format('DROP INDEX "%s"@"%s"', tableName, indexName);
    } else {
      sql = util.format('DROP INDEX "%s"', indexName);
    }

    return this.runSql(sql).nodeify(callback);
  },

  addIndex: function (tableName, indexName, columns, options, callback) {
    let unique = options === true;
    let inverted = '';

    if (typeof options === 'function') {
      callback = options;
    } else if (typeof options === 'object') {
      if (options.unique) unique = options.unique;
      if (options.inverted) inverted = 'INVERTED';
    }

    if (!Array.isArray(columns)) {
      columns = [columns];
    }
    var columnString = columns
      .map(column => {
        if (typeof column === 'object') {
          return (
            this.escapeDDL(column.name) +
            (column.DESC === true ? ' DESC' : ' ASC')
          );
        } else {
          return this.escapeDDL(column);
        }
      })
      .join(', ');

    var sql = util.format(
      'CREATE %s %s INDEX "%s" ON "%s" (%s)',
      unique ? 'UNIQUE' : '',
      inverted,
      indexName,
      tableName,
      columnString
    );

    return this.runSql(sql).nodeify(callback);
  },

  changePrimaryKey: function (table, newPrimary, callback) {
    return this.runSql(
      util.format(
        'ALTER TABLE %s DROP CONSTRAINT "primary", ADD PRIMARY KEY (%s)',
        this.escapeDDL(table),
        this.quoteDDLArr(newPrimary).join(', ')
      )
    ).nodeify(callback);
  },

  createMigrationsTable: function (callback) {
    var options = {
      columns: {
        id: {
          type: this.type.INTEGER,
          notNull: true,
          primaryKey: true,
          autoIncrement: true
        },
        name: { type: this.type.STRING, length: 255, notNull: true },
        run_on: { type: this.type.DATE_TIME, notNull: true }
      },
      ifNotExists: false
    };

    return this.all(
      "SELECT table_name FROM information_schema.tables WHERE table_name = '" +
        this.internals.migrationTable +
        "'" +
        (this.schema ? " AND table_catalog = '" + this.schema + "'" : '') +
        " AND table_schema = 'public'"
    )
      .then(
        function (result) {
          if (result && result.length < 1) {
            return this.createTable(this.internals.migrationTable, options);
          } else {
            return Promise.resolve();
          }
        }.bind(this)
      )
      .nodeify(callback);
  },

  learnable: {
    changePrimaryKey: function (t, n) {
      if (this.schema[t]) {
        const _n = Object.keys(this.schema[t]).reduce((o, x) => {
          const c = this.schema[t][x];
          if (c.primaryKey === true) {
            delete c.primaryKey;
            o.push(x);
          }

          return o;
        }, []);
        for (const col of n) {
          if (!this.schema[t][col]) {
            throw new Error(`There is no ${col} column in schema!`);
          }
          this.schema[t][col].primaryKey = true;
        }

        this.modC.push({ t: 0, a: 'changePrimaryKey', c: [t, _n] });
      }

      return Promise.resolve();
    }
  },

  statechanger: {
    changePrimaryKey: function () {
      return this._default();
    }
  },

  _meta: {
    supports: { optionParam: true, columnStrategies: true }
  }
});

exports.connect = function (config, intern, callback) {
  if (config.native) {
    pg = pg.native;
  }
  var db = config.db || new pg.Client(config);

  intern.interfaces.MigratorInterface.changePrimaryKey = dummy;

  db.connect(function (err) {
    if (err) {
      callback(err);
    }
    callback(null, new CockroachDriver(db, config.database, intern));
  });
};
<commit_msg>add enum<commit_after>const Base = require('db-migrate-pg').base;
const util = require('util');
let pg = require('pg');
const Promise = require('bluebird');

function dummy () {
  arguments[arguments.length - 1]('not implemented');
}

var CockroachDriver = Base.extend({
  init: function (connection, schema, intern) {
    this._super(connection, schema, intern);
  },

  addForeignKey: function (
    tableName,
    referencedTableName,
    keyName,
    fieldMapping,
    rules,
    callback
  ) {
    if (arguments.length === 5 && typeof rules === 'function') {
      callback = rules;
      rules = {};
    }
    var columns = Object.keys(fieldMapping);
    var referencedColumns = columns.map(function (key) {
      return '"' + fieldMapping[key] + '"';
    });
    var sql = util.format(
      'ALTER TABLE "%s" ADD CONSTRAINT "%s" FOREIGN KEY (%s) REFERENCES "%s" (%s) ON DELETE %s ON UPDATE %s',
      tableName,
      keyName,
      this.quoteDDLArr(columns).join(', '),
      referencedTableName,
      referencedColumns.join(', '),
      rules.onDelete || 'NO ACTION',
      rules.onUpdate || 'NO ACTION'
    );
    return this.runSql(sql).nodeify(callback);
  },

  _translateSpecialDefaultValues: function (
    spec,
    options,
    tableName,
    columnName
  ) {
    switch (spec.defaultValue.special) {
      case 'CURRENT_TIMESTAMP':
        spec.defaultValue.prep = 'CURRENT_TIMESTAMP()';
        break;
      default:
        this.super(spec, options, tableName, columnName);
        break;
    }
  },

  _handleMultiPrimaryKeys: function (primaryKeyColumns) {
    return util.format(
      ', PRIMARY KEY (%s)',
      this.quoteDDLArr(
        primaryKeyColumns
          .sort(function (a, b) {
            if (a.spec.interleave && b.spec.interleave) return 0;

            return a.spec.interleave ? -1 : 1;
          })
          .map(function (value) {
            return value.name;
          })
      ).join(', ')
    );
  },

  _applyTableOptions: function (options) {
    const columns = options.columns || options;
    const interleaves = [];
    const self = this;
    let sql = '';
    let interleave;

    Object.keys(columns).forEach(function (key) {
      var option = columns[key];

      if (option.interleave) {
        if (typeof option.interleave === 'string') {
          if (interleave && interleave !== option.interleave) {
            this.log.warn(
              'Ignoring interleave "' + interleave + '", you can only have one!'
            );
          } else {
            interleave = option.interleave;
          }

          interleaves.push(key);
        }
      }
    });

    if (interleaves.length > 0) {
      this.log.warn(
        'Interleaving has been deprecated in the latest cockroachdb releases. ' +
          'Make sure to remove them later if you did not already!'
      );
      sql = util.format(
        ' INTERLEAVE IN PARENT %s (%s)',
        self.escapeDDL(interleave),
        self.quoteDDLArr(interleaves).join(', ')
      );
    }

    return sql;
  },

  _applyExtensions: function (options) {
    const columns = options.columns || options;
    const families = {};
    const indizies = {};
    const sql = [];
    const self = this;
    let firstFamily;

    Object.keys(columns).forEach(function (key) {
      var option = columns[key];

      if (option.family && typeof option.family === 'string') {
        families[option.family] = families[option.family] || [];
        families[option.family].push(self.escapeDDL(key));

        if (option.primaryKey === true) firstFamily = option.family;
      }

      if (option.foreignKey && typeof option.foreignKey === 'object') {
        indizies[option.foreignKey.name] =
          indizies[option.foreignKey.name] || [];
        indizies[option.foreignKey.name].push(self.escapeDDL(key));
      }
    });

    Object.keys(indizies).forEach(function (key) {
      sql.push(
        util.format(
          'INDEX %s (%s)',
          self.escapeDDL(key),
          indizies[key].join(', ')
        )
      );
    });

    if (firstFamily) {
      sql.push(
        util.format(
          'FAMILY %s (%s)',
          self.escapeDDL(firstFamily),
          families[firstFamily].join(', ')
        )
      );
    }

    Object.keys(families).forEach(function (key) {
      if (key !== firstFamily) {
        sql.push(
          util.format(
            'FAMILY %s (%s)',
            self.escapeDDL(key),
            families[key].join(', ')
          )
        );
      }
    });

    if (sql.length === 0) return '';

    return ', ' + sql.join(', ');
  },

  changeColumn: function (tableName, columnName, columnSpec, callback) {
    let options = {};
    if (typeof callback === 'object') {
      options = callback;
      callback = null;
    }

    return setNotNull.call(this).nodeify(callback);

    function setNotNull () {
      if (columnSpec.notNull === undefined) {
        return setUnique.call(this);
      }
      var setOrDrop = columnSpec.notNull === true ? 'SET' : 'DROP';
      var sql = util.format(
        'ALTER TABLE "%s" ALTER COLUMN "%s" %s NOT NULL',
        tableName,
        columnName,
        setOrDrop
      );

      return this.runSql(sql).then(setUnique.bind(this));
    }

    function setUnique () {
      var sql;
      var constraintName = tableName + '_' + columnName + '_key';

      if (columnSpec.unique === true) {
        sql = util.format(
          'ALTER TABLE "%s" ADD CONSTRAINT "%s" UNIQUE ("%s")',
          tableName,
          constraintName,
          columnName
        );
        return this.runSql(sql).then(setDefaultValue.bind(this));
      } else if (columnSpec.unique === false) {
        sql = util.format(
          'DROP INDEX "%s"@"%s" CASCADE',
          tableName,
          constraintName
        );
        return this.runSql(sql).then(setDefaultValue.bind(this));
      } else {
        return setDefaultValue.call(this);
      }
    }

    function setDefaultValue () {
      var sql;

      if (columnSpec.defaultValue !== undefined) {
        var defaultValue = null;
        if (typeof columnSpec.defaultValue === 'string') {
          defaultValue = "'" + columnSpec.defaultValue + "'";
        } else {
          defaultValue = columnSpec.defaultValue;
        }
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" SET DEFAULT %s',
          tableName,
          columnName,
          defaultValue
        );
      } else {
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" DROP DEFAULT',
          tableName,
          columnName
        );
      }
      return this.runSql(sql).then(setType.bind(this));
    }

    async function setType () {
      // no changes are possible afterwards in cockroachdb currently
      let sql = '';
      if (columnSpec.type !== undefined) {
        sql = util.format(
          'ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s%s',
          tableName,
          columnName,
          columnSpec.type,
          columnSpec.length ? `(${columnSpec.length})` : ''
        );
      }

      if (sql === '') {
        return Promise.resolve();
      } else {
        return this.runSql(sql);
      }
    }
  },

  mapDataType: function (str) {
    str = str.toLowerCase();
    switch (str) {
      case 'float':
      case 'timestamptz':
      case 'uuid':
      case 'jsonb':
      case 'enum':
        return str;
      case 'computed':
        return '';
    }
    return this._super(str);
  },

  createColumnDef: function (name, spec, options, tableName) {
    // add support for datatype timetz, timestamptz
    // https://www.postgresql.org/docs/9.5/static/datatype.html
    spec.type = spec.type.replace(/^(time|timestamp)tz$/, function ($, type) {
      spec.timezone = true;
      return type;
    });
    var type =
      spec.primaryKey && spec.autoIncrement
        ? ''
        : this.mapDataType(spec.type, spec);

    if (type === 'enum') {
      type = spec.enumName;
    }

    var len = spec.length ? util.format('(%s)', spec.length) : '';
    var constraint = this.createColumnConstraint(
      spec,
      options,
      tableName,
      name
    );
    if (name.charAt(0) !== '"') {
      name = '"' + name + '"';
    }

    return {
      foreignKey: constraint.foreignKey,
      callbacks: constraint.callbacks,
      constraints: [name, type, len, constraint.constraints].join(' ')
    };
  },

  createColumnConstraint: function (spec, options, tableName, columnName) {
    var constraint = [];
    var callbacks = [];
    var cb;
    let type = spec.type;

    // this must keep first as it wilkl replace against mapDataType
    if (type.toLowerCase() === 'computed') {
      type = `${this.mapDataType(spec.computedType)} AS (${spec.function})`;
      if (spec.stored) {
        type += ' STORED';
      }
      constraint.push(type);
    }

    if (spec.primaryKey) {
      if (spec.autoIncrement) {
        if (this.mapDataType(spec.type) === 'uuid') {
          constraint.push('UUID');
          spec.defaultValue = { prep: 'gen_random_uuid()' };
        } else {
          constraint.push('SERIAL');
        }
      }

      if (options.emitPrimaryKey) {
        constraint.push('PRIMARY KEY');
      }
    }

    if (spec.timezone) {
      constraint.push('WITH TIME ZONE');
    }

    if (spec.notNull === true) {
      constraint.push('NOT NULL');
    }

    if (spec.unique) {
      constraint.push('UNIQUE');
    }

    if (spec.defaultValue !== undefined) {
      constraint.push('DEFAULT');
      if (typeof spec.defaultValue === 'string') {
        constraint.push("'" + spec.defaultValue + "'");
      } else if (spec.defaultValue.raw) {
        constraint.push(spec.defaultValue.raw);
      } else if (spec.defaultValue.prep) {
        constraint.push(spec.defaultValue.prep);
      } else {
        constraint.push(spec.defaultValue);
      }
    }

    // keep foreignKey for backward compatiable, push to callbacks in the future
    if (spec.foreignKey) {
      cb = this.bindForeignKey(tableName, columnName, spec.foreignKey);
    }
    if (spec.comment) {
      // TODO: create a new function addComment is not callable from here
      callbacks.push(
        function (tableName, columnName, comment, callback) {
          var sql = util.format(
            "COMMENT on COLUMN %s.%s IS '%s'",
            tableName,
            columnName,
            comment
          );
          return this.runSql(sql).nodeify(callback);
        }.bind(this, tableName, columnName, spec.comment)
      );
    }

    return {
      foreignKey: cb,
      callbacks: callbacks,
      constraints: constraint.join(' ')
    };
  },

  removeIndex: function (tableName, indexName, callback) {
    var sql;
    if (arguments.length === 2 && typeof indexName === 'function') {
      callback = indexName;
      indexName = tableName;
      tableName = null;
    } else if (arguments.length === 1 && typeof tableName === 'string') {
      indexName = tableName;
      tableName = null;
    }

    if (tableName) {
      sql = util.format('DROP INDEX "%s"@"%s"', tableName, indexName);
    } else {
      sql = util.format('DROP INDEX "%s"', indexName);
    }

    return this.runSql(sql).nodeify(callback);
  },

  addIndex: function (tableName, indexName, columns, options, callback) {
    let unique = options === true;
    let inverted = '';

    if (typeof options === 'function') {
      callback = options;
    } else if (typeof options === 'object') {
      if (options.unique) unique = options.unique;
      if (options.inverted) inverted = 'INVERTED';
    }

    if (!Array.isArray(columns)) {
      columns = [columns];
    }
    var columnString = columns
      .map(column => {
        if (typeof column === 'object') {
          return (
            this.escapeDDL(column.name) +
            (column.DESC === true ? ' DESC' : ' ASC')
          );
        } else {
          return this.escapeDDL(column);
        }
      })
      .join(', ');

    var sql = util.format(
      'CREATE %s %s INDEX "%s" ON "%s" (%s)',
      unique ? 'UNIQUE' : '',
      inverted,
      indexName,
      tableName,
      columnString
    );

    return this.runSql(sql).nodeify(callback);
  },

  changePrimaryKey: function (table, newPrimary, callback) {
    return this.runSql(
      util.format(
        'ALTER TABLE %s DROP CONSTRAINT "primary", ADD PRIMARY KEY (%s)',
        this.escapeDDL(table),
        this.quoteDDLArr(newPrimary).join(', ')
      )
    ).nodeify(callback);
  },

  createEnum: function (name, definition, callback) {
    return this.runSql(
      util.format(
        'CREATE TYPE %s AS ENUM (%s)',
        this.escapeDDL(name),
        this.quoteDDLArr(definition).join(', ')
      )
    ).nodeify(callback);
  },

  dropEnum: function (name, callback) {
    return this.runSql(
      util.format('DROP TYPE %s', this.escapeDDL(name))
    ).nodeify(callback);
  },

  createMigrationsTable: function (callback) {
    var options = {
      columns: {
        id: {
          type: this.type.INTEGER,
          notNull: true,
          primaryKey: true,
          autoIncrement: true
        },
        name: { type: this.type.STRING, length: 255, notNull: true },
        run_on: { type: this.type.DATE_TIME, notNull: true }
      },
      ifNotExists: false
    };

    return this.all(
      "SELECT table_name FROM information_schema.tables WHERE table_name = '" +
        this.internals.migrationTable +
        "'" +
        (this.schema ? " AND table_catalog = '" + this.schema + "'" : '') +
        " AND table_schema = 'public'"
    )
      .then(
        function (result) {
          if (result && result.length < 1) {
            return this.createTable(this.internals.migrationTable, options);
          } else {
            return Promise.resolve();
          }
        }.bind(this)
      )
      .nodeify(callback);
  },

  learnable: {
    changePrimaryKey: function (t, n) {
      if (this.schema[t]) {
        const _n = Object.keys(this.schema[t]).reduce((o, x) => {
          const c = this.schema[t][x];
          if (c.primaryKey === true) {
            delete c.primaryKey;
            o.push(x);
          }

          return o;
        }, []);
        for (const col of n) {
          if (!this.schema[t][col]) {
            throw new Error(`There is no ${col} column in schema!`);
          }
          this.schema[t][col].primaryKey = true;
        }

        this.modC.push({ t: 0, a: 'changePrimaryKey', c: [t, _n] });
      }

      return Promise.resolve();
    },

    createEnum: function (n, v) {
      if (!this.types) {
        this.types = {};
      }

      if (this.types[n] && this.types[n].t !== 'ENUM') {
        throw new Error(
          `This ENUM "${n}" already exists and collides with the ` +
            `type "${this.types[n].t}"`
        );
      }

      this.types[n] = { t: 'ENUM', v: JSON.parse(JSON.stringify(v)) };

      this.modC.push({ t: 0, a: 'dropEnum', c: [n, v] });

      return Promise.resolve();
    },

    dropEnum: function (n) {
      if (!this.types[n]) {
        throw new Error(`There is no such ENUM "${n}"`);
      }

      const v = this.types[n].v;
      delete this.types[n];

      this.modC.push({ t: 0, a: 'createEnum', c: [n, v] });

      return Promise.resolve();
    }
  },

  statechanger: {
    changePrimaryKey: function () {
      return this._default();
    },

    dropEnum: function () {
      return this._default();
    },

    createEnum: function () {
      return this._default();
    }
  },

  _meta: {
    supports: { optionParam: true, columnStrategies: true }
  }
});

exports.connect = function (config, intern, callback) {
  if (config.native) {
    pg = pg.native;
  }
  var db = config.db || new pg.Client(config);

  intern.interfaces.MigratorInterface.changePrimaryKey = dummy;
  intern.interfaces.MigratorInterface.dropEnum = dummy;
  intern.interfaces.MigratorInterface.createEnum = dummy;

  db.connect(function (err) {
    if (err) {
      callback(err);
    }
    callback(null, new CockroachDriver(db, config.database, intern));
  });
};
<|endoftext|>


<commit_before>module.exports = {
    managers: {
        auth: {
            AccountManager: require("./src/managers/auth/account-manager"),
            RoleManager: require("./src/managers/auth/role-manager")
        },
        master: {
            BuyerManager: require("./src/managers/master/buyer-manager"),
            SupplierManager: require("./src/managers/master/supplier-manager"),
            ProductManager: require("./src/managers/master/product-manager"),
            CategoryManager: require('./src/managers/master/category-manager'),
            DivisionManager: require('./src/managers/master/division-manager'),
            UnitManager: require('./src/managers/master/unit-manager'),
            UomManager: require('./src/managers/master/uom-manager'),
            CurrencyManager: require('./src/managers/master/currency-manager'),
            VatManager: require('./src/managers/master/vat-manager'),
            BudgetManager: require('./src/managers/master/budget-manager'),
            MachineManager: require('./src/managers/master/machine-manager'),
            Uster: require('./src/managers/master/uster-manager')
        },
        purchasing: {
            PurchaseOrderManager: require('./src/managers/purchasing/purchase-order-manager'),
            PurchaseOrderExternalManager: require('./src/managers/purchasing/purchase-order-external-manager'),
            DeliveryOrderManager: require('./src/managers/purchasing/delivery-order-manager'),
            UnitReceiptNoteManager: require('./src/managers/purchasing/unit-receipt-note-manager'),
            PurchaseRequestManager: require('./src/managers/purchasing/purchase-request-manager'),
            UnitPaymentPriceCorrectionNoteManager: require('./src/managers/purchasing/unit-payment-price-correction-note-manager'),
            UnitPaymentOrderManager: require('./src/managers/purchasing/unit-payment-order-manager')
        },
        production : {
            spinning : {
                winding : {
                    WindingQualitySampling : require('./src/managers/production/spinning/winding/winding-quality-sampling-manager')
                }
            }
        }
    }
}
<commit_msg>update index.js<commit_after>module.exports = {
    managers: {
        auth: {
            AccountManager: require("./src/managers/auth/account-manager"),
            RoleManager: require("./src/managers/auth/role-manager")
        },
        master: {
            BuyerManager: require("./src/managers/master/buyer-manager"),
            SupplierManager: require("./src/managers/master/supplier-manager"),
            ProductManager: require("./src/managers/master/product-manager"),
            CategoryManager: require('./src/managers/master/category-manager'),
            DivisionManager: require('./src/managers/master/division-manager'),
            UnitManager: require('./src/managers/master/unit-manager'),
            UomManager: require('./src/managers/master/uom-manager'),
            CurrencyManager: require('./src/managers/master/currency-manager'),
            VatManager: require('./src/managers/master/vat-manager'),
            BudgetManager: require('./src/managers/master/budget-manager'),
            MachineManager: require('./src/managers/master/machine-manager'),
            UsterManager: require('./src/managers/master/uster-manager')
        },
        purchasing: {
            PurchaseOrderManager: require('./src/managers/purchasing/purchase-order-manager'),
            PurchaseOrderExternalManager: require('./src/managers/purchasing/purchase-order-external-manager'),
            DeliveryOrderManager: require('./src/managers/purchasing/delivery-order-manager'),
            UnitReceiptNoteManager: require('./src/managers/purchasing/unit-receipt-note-manager'),
            PurchaseRequestManager: require('./src/managers/purchasing/purchase-request-manager'),
            UnitPaymentPriceCorrectionNoteManager: require('./src/managers/purchasing/unit-payment-price-correction-note-manager'),
            UnitPaymentOrderManager: require('./src/managers/purchasing/unit-payment-order-manager')
        },
        production : {
            spinning : {
                winding : {
                    WindingQualitySampling : require('./src/managers/production/spinning/winding/winding-quality-sampling-manager')
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>var through = require("through2");
var es = require("event-stream");
var path = require("path");
var fs = require('fs');

var scriptInject = function (param) {
  "use strict";

  var opts = param || {};
  var pathname = opts.path;
  var varname = opts.varname || 'yamlPath';
  var snippet = "<script></script>";

  var stream = through.obj(function (file, enc, callback) {
    var checkAndInsert = function(buffer) {
      return new Buffer(buffer.toString(enc).replace(/<\/head>/, function (w) {
        return snippet + w;
      }));
    };

    /**
     * Read JSON files
     */

    var jsonfiles = fs.readdirSync(path.resolve(pathname)),
        snippet = '<script id="'+varname+'">var '+ varname +' = ['

    for(var i =0; i< jsonfiles.length; i++){
        var name = jsonfiles[i].split('.')[0]

        snippet+="{name: \""+name+"\", path: \"json/" + jsonfiles[i]+ "\"},"

    }

    snippet = snippet.substring(0, snippet.length-1)

    snippet+="]</script>"

    if(file.isNull() || path.extname(file.path) !== '.html') {
      //Passthrough if not file or not html-file
      this.push(file);
      return callback();
    }

    if (file.contents instanceof Buffer) {

      file.contents = checkAndInsert(file.contents);

      this.push(file);
      return callback();

    } else {
      file.contents = file.contents.pipe(es.split("\n")).pipe(through(function(line, enc, callback) {

        line = checkAndInsert(line);

        this.push(line);
        callback();

      })).pipe(es.join("\n"));

      this.push(file);
      return callback();
    }
  });

  return stream;
};

module.exports = scriptInject;<commit_msg>Removed numbers from name<commit_after>var through = require("through2");
var es = require("event-stream");
var path = require("path");
var fs = require('fs');

var scriptInject = function (param) {
  "use strict";

  var opts = param || {};
  var pathname = opts.path;
  var varname = opts.varname || 'yamlPath';
  var snippet = "<script></script>";

  var stream = through.obj(function (file, enc, callback) {
    var checkAndInsert = function(buffer) {
      return new Buffer(buffer.toString(enc).replace(/<\/head>/, function (w) {
        return snippet + w;
      }));
    };

    /**
     * Read JSON files
     */

    var jsonfiles = fs.readdirSync(path.resolve(pathname)),
        snippet = '<script id="'+varname+'">var '+ varname +' = ['

    for(var i =0; i< jsonfiles.length; i++){
        var name = jsonfiles[i].replace(/^\d./, '').split('.')[0]

        snippet+="{name: \""+name+"\", path: \"json/" + jsonfiles[i]+ "\"},"

    }

    snippet = snippet.substring(0, snippet.length-1)

    snippet+="]</script>"

    if(file.isNull() || path.extname(file.path) !== '.html') {
      //Passthrough if not file or not html-file
      this.push(file);
      return callback();
    }

    if (file.contents instanceof Buffer) {

      file.contents = checkAndInsert(file.contents);

      this.push(file);
      return callback();

    } else {
      file.contents = file.contents.pipe(es.split("\n")).pipe(through(function(line, enc, callback) {

        line = checkAndInsert(line);

        this.push(line);
        callback();

      })).pipe(es.join("\n"));

      this.push(file);
      return callback();
    }
  });

  return stream;
};

module.exports = scriptInject;<|endoftext|>


<commit_before>var Ajax = require('simple-ajax');

module.exports = function(settings, callback){
    if(typeof settings === 'string'){
        settings = {
            url: settings
        };
    }

    if(typeof settings !== 'object'){
        throw 'settings must be a string or object';
    }

    if(typeof callback !== 'function'){
        throw 'cpjax must be passed a callback as the second parameter';
    }

    var ajax = new Ajax(settings);

    ajax.on('success', function(event, data) {
        callback(null, data, event);
    });
    ajax.on('error', function(event) {
        callback(event.target.responseText, null, event);
    });

    ajax.send();

    return ajax;
};<commit_msg>Always return error instances on error<commit_after>var Ajax = require('simple-ajax');

module.exports = function(settings, callback){
    if(typeof settings === 'string'){
        settings = {
            url: settings
        };
    }

    if(typeof settings !== 'object'){
        throw 'settings must be a string or object';
    }

    if(typeof callback !== 'function'){
        throw 'cpjax must be passed a callback as the second parameter';
    }

    var ajax = new Ajax(settings);

    ajax.on('success', function(event, data) {
        callback(null, data, event);
    });
    ajax.on('error', function(event) {
        callback(new Error(event.target.responseText), null, event);
    });

    ajax.send();

    return ajax;
};<|endoftext|>


<commit_before>;(function (root, factory) {
    var namespace = 'VirtualScrollList'
    if (typeof exports === 'object' && typeof module === 'object') {
        module.exports = factory(namespace, require('vue'))
    } else if (typeof define === 'function' && define.amd) {
        define(['vue'], factory.bind(root, namespace))
    } else if (typeof exports === 'object') {
        exports[namespace] = factory(namespace, require('vue'))
    } else {
        root[namespace] = factory(namespace, root['Vue'])
    }
})(this, function (namespace, Vue2) {
    if (typeof Vue2 === 'object' && typeof Vue2.default === 'function') {
        Vue2 = Vue2.default
    }

    var _debounce = function (func, wait, immediate) {
        var timeout
        return function () {
            var context = this
            var args = arguments
            var later = function () {
                timeout = null
                if (!immediate) {
                    func.apply(context, args)
                }
            }
            var callNow = immediate && !timeout
            clearTimeout(timeout)
            timeout = setTimeout(later, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
    }

    return Vue2.component(namespace, {
        props: {
            size: { type: Number, required: true },
            remain: { type: Number, required: true },
            rtag: { type: String, default: 'div' },
            wtag: { type: String, default: 'div' },
            wclass: { type: String, default: '' },
            start: { type: Number, default: 0 },
            offset: { type: Number, default: 0 },
            variable: [Function, Boolean],
            bench: Number,
            debounce: Number,
            totop: Function,
            tobottom: Function,
            onscroll: Function
        },

        created: function () {
            var start = this.start >= this.remain ? this.start : 0
            var keeps = this.remain + (this.bench || this.remain)

            this.delta = {
                start: start, // start index.
                end: start + keeps - 1, // end index.
                keeps: keeps, // nums keeping in real dom.
                total: 0, // all items count, update in filter.
                offsetAll: 0, // cache all the scrollable offset.
                paddingTop: 0, // container wrapper real padding-top.
                paddingBottom: 0, // container wrapper real padding-bottom.
                varCache: {}, // object to cache variable index height and scroll offset.
                varAverSize: 0, // average/estimate item height before variable be calculated.
                varLastCalcIndex: 0 // last calculated variable height/offset index, always increase.
            }
        },

        watch: {
            size: function () {
                this.alter = 'size'
            },
            remain: function () {
                this.alter = 'remain'
            },
            bench: function () {
                this.alter = 'bench'
            },
            start: function () {
                this.alter = 'start'
            },
            offset: function () {
                this.alter = 'offset'
            }
        },

        methods: {
            onScroll: function (e) {
                var delta = this.delta
                var offset = this.$refs.vsl.scrollTop

                if (delta.total > delta.keeps) {
                    this.updateZone(offset)
                } else {
                    delta.end = delta.total - 1
                }

                var offsetAll = delta.offsetAll
                if (this.onscroll) {
                    this.onscroll(e, {
                        offset: offset,
                        offsetAll: offsetAll,
                        start: delta.start,
                        end: delta.end
                    })
                }

                if (!offset && delta.total) {
                    this.triggerEvent('totop')
                }

                if (offset >= offsetAll) {
                    this.triggerEvent('tobottom')
                }
            },

            // update render zone by scroll offset.
            updateZone: function (offset) {
                var overs = this.variable
                    ? this.getVarOvers(offset)
                    : Math.floor(offset / this.size)

                var delta = this.delta
                var zone = this.getZone(overs)
                var bench = this.bench || this.remain

                // for better performance, if scroll pass items within now bench, do not update.
                if (!zone.isLast && (overs > delta.start) && (overs - delta.start <= bench)) {
                    return
                }

                delta.end = zone.end
                delta.start = zone.start
                this.$forceUpdate()
            },

            // return the scroll passed items count in variable.
            getVarOvers: function (offset) {
                var low = 0
                var middle = 0
                var middleOffset = 0
                var delta = this.delta
                var high = delta.total

                while (low <= high) {
                    middle = low + Math.floor((high - low) / 2)
                    middleOffset = this.getVarOffset(middle)

                    // calculate the average variable height at first binary search.
                    if (!delta.varAverSize) {
                        delta.varAverSize = Math.floor(middleOffset / middle)
                    }

                    if (middleOffset === offset) {
                        return middle
                    } else if (middleOffset < offset) {
                        low = middle + 1
                    } else if (middleOffset > offset) {
                        high = middle - 1
                    }
                }

                return low > 0 ? --low : 0
            },

            // return a variable scroll offset from given index.
            getVarOffset: function (index, nocache) {
                var delta = this.delta
                var cache = delta.varCache[index]

                if (!nocache && cache) {
                    return cache.offset
                }

                var offset = 0
                for (var i = 0; i < index; i++) {
                    var size = this.getVarSize(i, nocache)
                    delta.varCache[i] = {
                        size: size,
                        offset: offset
                    }
                    offset += size
                }

                delta.varLastCalcIndex = Math.max(delta.varLastCalcIndex, index - 1)
                delta.varLastCalcIndex = Math.min(delta.varLastCalcIndex, delta.total - 1)

                return offset
            },

            // return a variable size (height) from given index.
            getVarSize: function (index, nocache) {
                var cache = this.delta.varCache[index]
                if (!nocache && cache) {
                    return cache.size
                }

                if (typeof this.variable === 'function') {
                    return this.variable(index) || 0
                } else {
                    var slot = this.$slots.default[index]
                    var style = slot && slot.data && slot.data.style
                    if (style && style.height) {
                        var shm = style.height.match(/^(.*)px$/)
                        return (shm && +shm[1]) || 0
                    }
                }
                return 0
            },

            // return the variable paddingTop base current zone.
            // @todo: if set a large `start` before variable was calculated,
            // here will also case too much offset calculate when list is very large,
            // consider use estimate paddingTop in this case just like `getVarPaddingBottom`.
            getVarPaddingTop: function () {
                return this.getVarOffset(this.delta.start)
            },

            // return the variable paddingBottom base current zone.
            getVarPaddingBottom: function () {
                var delta = this.delta
                var last = delta.total - 1
                if (delta.total - delta.end <= delta.keeps || delta.varLastCalcIndex === last) {
                    return this.getVarOffset(last) - this.getVarOffset(delta.end)
                } else {
                    // if unreached last zone or uncalculate real behind offset
                    // return the estimate paddingBottom avoid too much calculate.
                    return (delta.total - delta.end) * (delta.varAverSize || this.size)
                }
            },

            // retun the variable all heights use to judge reach bottom.
            getVarAllHeight: function () {
                var delta = this.delta
                if (delta.total - delta.end <= delta.keeps || delta.varLastCalcIndex === delta.total - 1) {
                    return this.getVarOffset(delta.total)
                } else {
                    return this.getVarOffset(delta.start) + (delta.total - delta.end) * (delta.varAverSize || this.size)
                }
            },

            // the ONLY ONE public method, allow the parent update variable by index.
            updateVariable: function (index) {
                // clear/update all the offfsets and heights ahead of index.
                this.getVarOffset(index, true)
            },

            // return the right zone info base on `start/index`.
            getZone: function (index) {
                var start, end
                var delta = this.delta

                index = parseInt(index, 10)
                index = Math.max(0, index)

                var lastStart = delta.total - delta.keeps
                var isLast = (index <= delta.total && index >= lastStart) || (index > delta.total)
                if (isLast) {
                    end = delta.total - 1
                    start = Math.max(0, lastStart)
                } else {
                    start = index
                    end = start + delta.keeps - 1
                }

                return {
                    end: end,
                    start: start,
                    isLast: isLast
                }
            },

            // trigger a props event on parent.
            triggerEvent: function (event) {
                if (this[event]) {
                    this[event]()
                }
            },

            // set manual scroll top.
            setScrollTop: function (scrollTop) {
                this.$refs.vsl.scrollTop = scrollTop
            },

            // filter the shown items base on `start` and `end`.
            filter: function () {
                var delta = this.delta
                var slots = this.$slots.default

                if (!slots) {
                    slots = []
                    delta.start = 0
                }

                delta.total = slots.length

                var paddingTop, paddingBottom, allHeight
                var hasPadding = delta.total > delta.keeps

                if (this.variable) {
                    allHeight = this.getVarAllHeight()
                    paddingTop = hasPadding ? this.getVarPaddingTop() : 0
                    paddingBottom = hasPadding ? this.getVarPaddingBottom() : 0
                } else {
                    allHeight = this.size * delta.total
                    paddingTop = this.size * (hasPadding ? delta.start : 0)
                    paddingBottom = this.size * (hasPadding ? delta.total - delta.keeps : 0) - paddingTop
                }

                delta.paddingTop = paddingTop
                delta.paddingBottom = paddingBottom
                delta.offsetAll = allHeight - this.size * this.remain

                return slots.filter(function (slot, index) {
                    return index >= delta.start && index <= delta.end
                })
            }
        },

        mounted: function () {
            if (this.start) {
                var start = this.getZone(this.start).start
                this.setScrollTop(this.variable ? this.getVarOffset(start) : start * this.size)
            } else if (this.offset) {
                this.setScrollTop(this.offset)
            }
        },

        // check if delta should update when prorps change.
        beforeUpdate: function () {
            var delta = this.delta
            delta.keeps = this.remain + (this.bench || this.remain)

            var calcstart = this.alter === 'start' ? this.start : delta.start
            var zone = this.getZone(calcstart)

            // if start, size or offset change, update scroll position.
            if (~['start', 'size', 'offset'].indexOf(this.alter)) {
                this.$nextTick(this.setScrollTop.bind(this, this.alter === 'offset'
                    ? this.offset : this.variable
                        ? this.getVarOffset(zone.isLast ? delta.total : zone.start)
                        : zone.isLast && (delta.total - calcstart <= this.remain)
                            ? delta.total * this.size : calcstart * this.size)
                )
            }

            // if points out difference, force update once again.
            if (calcstart !== zone.start || delta.end !== zone.end || this.alter) {
                this.alter = ''
                delta.end = zone.end
                delta.start = zone.start
                this.$forceUpdate()
            }
        },

        render: function (h) {
            var list = this.filter()
            var delta = this.delta
            var dbc = this.debounce

            return h(this.rtag, {
                'ref': 'vsl',
                'style': {
                    'display': 'block',
                    'overflow-y': 'auto',
                    'height': this.size * this.remain + 'px'
                },
                'on': {
                    'scroll': dbc ? _debounce(this.onScroll.bind(this), dbc) : this.onScroll
                }
            }, [
                h(this.wtag, {
                    'style': {
                        'display': 'block',
                        'padding-top': delta.paddingTop + 'px',
                        'padding-bottom': delta.paddingBottom + 'px'
                    },
                    'class': this.wclass
                }, list)
            ])
        }
    })
})
<commit_msg>Mark the onscroll as passive<commit_after>;(function (root, factory) {
    var namespace = 'VirtualScrollList'
    if (typeof exports === 'object' && typeof module === 'object') {
        module.exports = factory(namespace, require('vue'))
    } else if (typeof define === 'function' && define.amd) {
        define(['vue'], factory.bind(root, namespace))
    } else if (typeof exports === 'object') {
        exports[namespace] = factory(namespace, require('vue'))
    } else {
        root[namespace] = factory(namespace, root['Vue'])
    }
})(this, function (namespace, Vue2) {
    if (typeof Vue2 === 'object' && typeof Vue2.default === 'function') {
        Vue2 = Vue2.default
    }

    var _debounce = function (func, wait, immediate) {
        var timeout
        return function () {
            var context = this
            var args = arguments
            var later = function () {
                timeout = null
                if (!immediate) {
                    func.apply(context, args)
                }
            }
            var callNow = immediate && !timeout
            clearTimeout(timeout)
            timeout = setTimeout(later, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
    }

    return Vue2.component(namespace, {
        props: {
            size: { type: Number, required: true },
            remain: { type: Number, required: true },
            rtag: { type: String, default: 'div' },
            wtag: { type: String, default: 'div' },
            wclass: { type: String, default: '' },
            start: { type: Number, default: 0 },
            offset: { type: Number, default: 0 },
            variable: [Function, Boolean],
            bench: Number,
            debounce: Number,
            totop: Function,
            tobottom: Function,
            onscroll: Function
        },

        created: function () {
            var start = this.start >= this.remain ? this.start : 0
            var keeps = this.remain + (this.bench || this.remain)

            this.delta = {
                start: start, // start index.
                end: start + keeps - 1, // end index.
                keeps: keeps, // nums keeping in real dom.
                total: 0, // all items count, update in filter.
                offsetAll: 0, // cache all the scrollable offset.
                paddingTop: 0, // container wrapper real padding-top.
                paddingBottom: 0, // container wrapper real padding-bottom.
                varCache: {}, // object to cache variable index height and scroll offset.
                varAverSize: 0, // average/estimate item height before variable be calculated.
                varLastCalcIndex: 0 // last calculated variable height/offset index, always increase.
            }
        },

        watch: {
            size: function () {
                this.alter = 'size'
            },
            remain: function () {
                this.alter = 'remain'
            },
            bench: function () {
                this.alter = 'bench'
            },
            start: function () {
                this.alter = 'start'
            },
            offset: function () {
                this.alter = 'offset'
            }
        },

        methods: {
            onScroll: function (e) {
                var delta = this.delta
                var offset = this.$refs.vsl.scrollTop

                if (delta.total > delta.keeps) {
                    this.updateZone(offset)
                } else {
                    delta.end = delta.total - 1
                }

                var offsetAll = delta.offsetAll
                if (this.onscroll) {
                    this.onscroll(e, {
                        offset: offset,
                        offsetAll: offsetAll,
                        start: delta.start,
                        end: delta.end
                    })
                }

                if (!offset && delta.total) {
                    this.triggerEvent('totop')
                }

                if (offset >= offsetAll) {
                    this.triggerEvent('tobottom')
                }
            },

            // update render zone by scroll offset.
            updateZone: function (offset) {
                var overs = this.variable
                    ? this.getVarOvers(offset)
                    : Math.floor(offset / this.size)

                var delta = this.delta
                var zone = this.getZone(overs)
                var bench = this.bench || this.remain

                // for better performance, if scroll pass items within now bench, do not update.
                if (!zone.isLast && (overs > delta.start) && (overs - delta.start <= bench)) {
                    return
                }

                delta.end = zone.end
                delta.start = zone.start
                this.$forceUpdate()
            },

            // return the scroll passed items count in variable.
            getVarOvers: function (offset) {
                var low = 0
                var middle = 0
                var middleOffset = 0
                var delta = this.delta
                var high = delta.total

                while (low <= high) {
                    middle = low + Math.floor((high - low) / 2)
                    middleOffset = this.getVarOffset(middle)

                    // calculate the average variable height at first binary search.
                    if (!delta.varAverSize) {
                        delta.varAverSize = Math.floor(middleOffset / middle)
                    }

                    if (middleOffset === offset) {
                        return middle
                    } else if (middleOffset < offset) {
                        low = middle + 1
                    } else if (middleOffset > offset) {
                        high = middle - 1
                    }
                }

                return low > 0 ? --low : 0
            },

            // return a variable scroll offset from given index.
            getVarOffset: function (index, nocache) {
                var delta = this.delta
                var cache = delta.varCache[index]

                if (!nocache && cache) {
                    return cache.offset
                }

                var offset = 0
                for (var i = 0; i < index; i++) {
                    var size = this.getVarSize(i, nocache)
                    delta.varCache[i] = {
                        size: size,
                        offset: offset
                    }
                    offset += size
                }

                delta.varLastCalcIndex = Math.max(delta.varLastCalcIndex, index - 1)
                delta.varLastCalcIndex = Math.min(delta.varLastCalcIndex, delta.total - 1)

                return offset
            },

            // return a variable size (height) from given index.
            getVarSize: function (index, nocache) {
                var cache = this.delta.varCache[index]
                if (!nocache && cache) {
                    return cache.size
                }

                if (typeof this.variable === 'function') {
                    return this.variable(index) || 0
                } else {
                    var slot = this.$slots.default[index]
                    var style = slot && slot.data && slot.data.style
                    if (style && style.height) {
                        var shm = style.height.match(/^(.*)px$/)
                        return (shm && +shm[1]) || 0
                    }
                }
                return 0
            },

            // return the variable paddingTop base current zone.
            // @todo: if set a large `start` before variable was calculated,
            // here will also case too much offset calculate when list is very large,
            // consider use estimate paddingTop in this case just like `getVarPaddingBottom`.
            getVarPaddingTop: function () {
                return this.getVarOffset(this.delta.start)
            },

            // return the variable paddingBottom base current zone.
            getVarPaddingBottom: function () {
                var delta = this.delta
                var last = delta.total - 1
                if (delta.total - delta.end <= delta.keeps || delta.varLastCalcIndex === last) {
                    return this.getVarOffset(last) - this.getVarOffset(delta.end)
                } else {
                    // if unreached last zone or uncalculate real behind offset
                    // return the estimate paddingBottom avoid too much calculate.
                    return (delta.total - delta.end) * (delta.varAverSize || this.size)
                }
            },

            // retun the variable all heights use to judge reach bottom.
            getVarAllHeight: function () {
                var delta = this.delta
                if (delta.total - delta.end <= delta.keeps || delta.varLastCalcIndex === delta.total - 1) {
                    return this.getVarOffset(delta.total)
                } else {
                    return this.getVarOffset(delta.start) + (delta.total - delta.end) * (delta.varAverSize || this.size)
                }
            },

            // the ONLY ONE public method, allow the parent update variable by index.
            updateVariable: function (index) {
                // clear/update all the offfsets and heights ahead of index.
                this.getVarOffset(index, true)
            },

            // return the right zone info base on `start/index`.
            getZone: function (index) {
                var start, end
                var delta = this.delta

                index = parseInt(index, 10)
                index = Math.max(0, index)

                var lastStart = delta.total - delta.keeps
                var isLast = (index <= delta.total && index >= lastStart) || (index > delta.total)
                if (isLast) {
                    end = delta.total - 1
                    start = Math.max(0, lastStart)
                } else {
                    start = index
                    end = start + delta.keeps - 1
                }

                return {
                    end: end,
                    start: start,
                    isLast: isLast
                }
            },

            // trigger a props event on parent.
            triggerEvent: function (event) {
                if (this[event]) {
                    this[event]()
                }
            },

            // set manual scroll top.
            setScrollTop: function (scrollTop) {
                this.$refs.vsl.scrollTop = scrollTop
            },

            // filter the shown items base on `start` and `end`.
            filter: function () {
                var delta = this.delta
                var slots = this.$slots.default

                if (!slots) {
                    slots = []
                    delta.start = 0
                }

                delta.total = slots.length

                var paddingTop, paddingBottom, allHeight
                var hasPadding = delta.total > delta.keeps

                if (this.variable) {
                    allHeight = this.getVarAllHeight()
                    paddingTop = hasPadding ? this.getVarPaddingTop() : 0
                    paddingBottom = hasPadding ? this.getVarPaddingBottom() : 0
                } else {
                    allHeight = this.size * delta.total
                    paddingTop = this.size * (hasPadding ? delta.start : 0)
                    paddingBottom = this.size * (hasPadding ? delta.total - delta.keeps : 0) - paddingTop
                }

                delta.paddingTop = paddingTop
                delta.paddingBottom = paddingBottom
                delta.offsetAll = allHeight - this.size * this.remain

                return slots.filter(function (slot, index) {
                    return index >= delta.start && index <= delta.end
                })
            }
        },

        mounted: function () {
            if (this.start) {
                var start = this.getZone(this.start).start
                this.setScrollTop(this.variable ? this.getVarOffset(start) : start * this.size)
            } else if (this.offset) {
                this.setScrollTop(this.offset)
            }
        },

        // check if delta should update when prorps change.
        beforeUpdate: function () {
            var delta = this.delta
            delta.keeps = this.remain + (this.bench || this.remain)

            var calcstart = this.alter === 'start' ? this.start : delta.start
            var zone = this.getZone(calcstart)

            // if start, size or offset change, update scroll position.
            if (~['start', 'size', 'offset'].indexOf(this.alter)) {
                this.$nextTick(this.setScrollTop.bind(this, this.alter === 'offset'
                    ? this.offset : this.variable
                        ? this.getVarOffset(zone.isLast ? delta.total : zone.start)
                        : zone.isLast && (delta.total - calcstart <= this.remain)
                            ? delta.total * this.size : calcstart * this.size)
                )
            }

            // if points out difference, force update once again.
            if (calcstart !== zone.start || delta.end !== zone.end || this.alter) {
                this.alter = ''
                delta.end = zone.end
                delta.start = zone.start
                this.$forceUpdate()
            }
        },

        render: function (h) {
            var list = this.filter()
            var delta = this.delta
            var dbc = this.debounce

            return h(this.rtag, {
                'ref': 'vsl',
                'style': {
                    'display': 'block',
                    'overflow-y': 'auto',
                    'height': this.size * this.remain + 'px'
                },
                'on': {
                    '&scroll': dbc ? _debounce(this.onScroll.bind(this), dbc) : this.onScroll
                }
            }, [
                h(this.wtag, {
                    'style': {
                        'display': 'block',
                        'padding-top': delta.paddingTop + 'px',
                        'padding-bottom': delta.paddingBottom + 'px'
                    },
                    'class': this.wclass
                }, list)
            ])
        }
    })
})
<|endoftext|>


<commit_before>const defaultConfig = require('./lib/default-config')
const getContents = require('./lib/get-file-contents')
const generateBody = require('./lib/generate-body')
const commitIsInPR = require('./lib/commit-is-in-pr')
const ssrTemplate = require('./lib/ssr-template')
const React = require('react')
const ReactDOMServer = require('react-dom/server')
const App = require('./public/server.min.js').default

const express = require('express')
const path = require('path')

module.exports = (robot) => {
  const app = robot.route('/')
  app.use(express.static(path.join(__dirname, 'public')))
  app.get('/', (req, res) => {
    const ssrStr = ReactDOMServer.renderToString(React.createElement(App))
    const tpl = ssrTemplate(ssrStr)

    /* istanbul ignore next */
    if (process.env.NODE_ENV !== 'development') {
      res.setHeader('cache-control', 'public, max-age=1200, s-maxage=3200')
    }

    res.end(tpl)
  })

  robot.on('push', async context => {
    const config = await context.config('config.yml')
    const cfg = {...defaultConfig, ...config.todo}

    // Get array of issue objects in the current repo
    const issues = await context.github.issues.getForRepo(context.repo())

    const {head_commit, commits} = context.payload
    const author = head_commit.author.username

    // Get the most up-to-date contents of each file
    // by the commit it was most recently edited in.
    const commitsByFiles = new Map()
    for (let c = 0; c < commits.length; c++) {
      const commit = commits[c]
      const files = [...commit.added, ...commit.modified]
      const mappedFiles = new Map()

      for (let i = 0; i < files.length; i++) {
        const file = files[i]
        const sliced = commits.slice(c + 1)

        if (sliced.every(com => com.modified.indexOf(file) === -1)) {
          const contents = await getContents(context, commit.id, file)
          mappedFiles.set(file, contents)
        }
      }

      commitsByFiles.set(commit.id, mappedFiles)
    }

    commitsByFiles.forEach(async (files, sha) => {
      files.forEach(async (contents, file) => {
        // Get issue titles
        const re = new RegExp(`${cfg.keyword}\\s(.*)`, cfg.caseSensitive ? 'g' : 'gi')
        const matches = contents.match(re)
        if (!matches) return

        const titles = matches.map(title => title.replace(`${cfg.keyword} `, ''))
        titles.forEach(async title => {
          // Check if an issue with that title exists
          if (issues.data.some(issue => issue.title === title && issue.state === 'open')) return

          // :TODO: Reopen existing but closed issues if the same todo is introduced

          const pr = await commitIsInPR(context, sha)
          const body = generateBody(context, cfg, title, file, contents, author, sha, pr)

          // Generate an issue object
          // :TODO: Add labels

          const issueObj = { title, body }
          if (cfg.autoAssign === true) {
            issueObj.assignee = author
          } else if (typeof cfg.autoAssign === 'string') {
            issueObj.assignee = cfg.autoAssign
          } else if (Array.isArray(cfg.autoAssign)) {
            issueObj.assignees = cfg.autoAssign
          }

          const issue = context.issue(issueObj)

          return context.github.issues.create(issue)
        })
      })
    })
  })
}
<commit_msg>Render React string at runtime<commit_after>const defaultConfig = require('./lib/default-config')
const getContents = require('./lib/get-file-contents')
const generateBody = require('./lib/generate-body')
const commitIsInPR = require('./lib/commit-is-in-pr')
const ssrTemplate = require('./lib/ssr-template')
const React = require('react')
const ReactDOMServer = require('react-dom/server')
const App = require('./public/server.min.js').default

const express = require('express')
const path = require('path')

const ssrStr = ReactDOMServer.renderToString(React.createElement(App))
const tpl = ssrTemplate(ssrStr)

module.exports = (robot) => {
  const app = robot.route('/')
  app.use(express.static(path.join(__dirname, 'public')))
  app.get('/', (req, res) => {
    /* istanbul ignore next */
    if (process.env.NODE_ENV !== 'development') {
      res.setHeader('cache-control', 'public, max-age=1200, s-maxage=3200')
    }

    res.end(tpl)
  })

  robot.on('push', async context => {
    const config = await context.config('config.yml')
    const cfg = {...defaultConfig, ...config.todo}

    // Get array of issue objects in the current repo
    const issues = await context.github.issues.getForRepo(context.repo())

    const {head_commit, commits} = context.payload
    const author = head_commit.author.username

    // Get the most up-to-date contents of each file
    // by the commit it was most recently edited in.
    const commitsByFiles = new Map()
    for (let c = 0; c < commits.length; c++) {
      const commit = commits[c]
      const files = [...commit.added, ...commit.modified]
      const mappedFiles = new Map()

      for (let i = 0; i < files.length; i++) {
        const file = files[i]
        const sliced = commits.slice(c + 1)

        if (sliced.every(com => com.modified.indexOf(file) === -1)) {
          const contents = await getContents(context, commit.id, file)
          mappedFiles.set(file, contents)
        }
      }

      commitsByFiles.set(commit.id, mappedFiles)
    }

    commitsByFiles.forEach(async (files, sha) => {
      files.forEach(async (contents, file) => {
        // Get issue titles
        const re = new RegExp(`${cfg.keyword}\\s(.*)`, cfg.caseSensitive ? 'g' : 'gi')
        const matches = contents.match(re)
        if (!matches) return

        const titles = matches.map(title => title.replace(`${cfg.keyword} `, ''))
        titles.forEach(async title => {
          // Check if an issue with that title exists
          if (issues.data.some(issue => issue.title === title && issue.state === 'open')) return

          // :TODO: Reopen existing but closed issues if the same todo is introduced

          const pr = await commitIsInPR(context, sha)
          const body = generateBody(context, cfg, title, file, contents, author, sha, pr)

          // Generate an issue object
          // :TODO: Add labels

          const issueObj = { title, body }
          if (cfg.autoAssign === true) {
            issueObj.assignee = author
          } else if (typeof cfg.autoAssign === 'string') {
            issueObj.assignee = cfg.autoAssign
          } else if (Array.isArray(cfg.autoAssign)) {
            issueObj.assignees = cfg.autoAssign
          }

          const issue = context.issue(issueObj)

          return context.github.issues.create(issue)
        })
      })
    })
  })
}
<|endoftext|>


<commit_before>'use strict';

var argv = require('yargs').argv;
var conf = require('confucious');
var path = require('path');
var fs = require('fs-extra');
var globby = require('globby');
var chalk = require('chalk');
var validate = require('./validate');
var S = require('string');
var AsciiTable = require('ascii-table');

var workingDirectory = process.cwd();
var buildFilePath = path.join(workingDirectory, "build.js");
var tasksDirectory = path.join(workingDirectory, 'tasks');

//
// task-mule init
//
var commandInit = function (log) {

	if (fs.existsSync(buildFilePath)) {
		log.error("Can't overwrite existing 'build.js'.");
		process.exit(1);
	}

	// Auto create build.js.
	var defaultBuildJs = path.join(__dirname, 'build.js');
	fs.copySync(defaultBuildJs, buildFilePath);
	log.info("Created new 'build.js' at " + buildFilePath);
	process.exit(0);
};

//
// task-mule create-task <task-name>
//
var commandCreateTask = function (log) {

	var newTaskName = argv._[1];
	if (!newTaskName) {
		log.error("Task name not specified.");
		process.exit(1);
	}

	if (!S(newTaskName.toLowerCase()).endsWith(".js")) {
		if (newTaskName[newTaskName.length-1] === '.') {
			// Trim final period.
			newTaskName = newTaskName.substring(0, newTaskName.length-1);
		}

		// Auto add extension.
		newTaskName += ".js";
	}

	var newTaskFilePath = path.join(tasksDirectory, newTaskName);
	if (fs.existsSync(newTaskFilePath)) {
		log.error("Can't create task, file already exists: " + newTaskFilePath);
		process.exit(1);
	}

	var defaultTaskFile = path.join(__dirname, 'default-task.js');
	fs.copySync(defaultTaskFile, newTaskFilePath);
	log.info("Created new task file at " + newTaskFilePath);
};

//
// Init config prior to running or listing tasks.
//
var initConfig = function () {

	var buildConfig = require(buildFilePath)(conf, log, validate);

	var defaultConfigFilePath = path.join(workingDirectory, 'config.json');
	if (fs.existsSync(defaultConfigFilePath)) {

		log.verbose("Loading config from file: " + defaultConfigFilePath);

		conf.pushJsonFile(defaultConfigFilePath);
	}

	conf.pushEnv();
	conf.pushArgv();

	if (config.defaultConfig) {
		conf.push(config.defaultConfig)
	}

	buildConfig.init();
};

//
// task-mule schedule
//
var commandSchedule = function (log) {

	if (!fs.existsSync('schedule.json')) {
		log.error('Expected scehdule.json to specify the schedule of tasks.');
		process.exit(1);
	}

	initConfig();

	var taskRunner = require('./task-loader.js')({}, log, validate, conf);

	var schedule = JSON.parse(fs.readFileSync('schedule.json', 'utf8'));

	var taskScheduler = require('task-scheduler');
	taskScheduler.start(schedule, config.schedulerCallbacks);
};

//
// task-mule <task-name>
//
var commandRunTask = function (log, requestedTaskName) {

	if (!fs.existsSync(buildFilePath)) {
		log.error("'build.js' not found, please run task-mule in a directory that has this file.");
		log.info("Run 'task-mule init' to create a default 'build.js'.")
		process.exit(1);
	}

	if (!fs.existsSync(tasksDirectory)) {
		log.error("'tasks' directory doesn't exist.");
		log.info("Run 'task-mule create-task <task-name> to create your first task.");
		process.exit(1);
	}

	initConfig();

	var taskRunner = require('./task-loader.js')({}, log, validate, conf);

	if (requestedTaskName) {
	    return taskRunner.runTask(requestedTaskName, conf)
            .catch(function (err) {

                log.error('Build failed.');

                if (err.message) {
                    log.warn(err.message);
                }

                if (err.stack) {
                    log.warn(err.stack);
                }
                else {
                    log.warn('no stack');
                }
                if (!config.noExit) {
                	process.exit(1);
                }

                buildConfig.done();

                throw err;
            })
	        .then(function () {
        		buildConfig.done();
	        });
	}
	else if (argv.tasks) {
	    return taskRunner.resolveAllDependencies(conf)
	    	.then(function () {
			    taskRunner.listTasks();
			    process.exit(1);
			});
	}
	else {
	    log.info("Usage: task-mule <task-name> [options]\n");

	    var optionsTable = new AsciiTable('Options');
	    optionsTable
	      .setHeading('Options', 'Description');

	    buildConfig.options.forEach(function (option) {
	        optionsTable.addRow(option[0], option[1]);
	    });

	    console.log(chalk.bold.green(optionsTable.toString()));

	    var examplesTable = new AsciiTable('Examples');
	    examplesTable.setHeading('What?', 'Command Line');

	     buildConfig.examples.forEach(function (example) {
	     	examplesTable.addRow(example[0], example[1]);
	     });

	    console.log(chalk.bold.green(examplesTable.toString()));

	    process.exit(1);
	}
};

module.exports = function (config) {

	config = config || {};

	var log = config.log || require('./log')(argv.verbose, argv.nocolors);

	global.runCmd = require('./run-cmd')(log);
	global.path = require('path');
	global.fs = require('fs-extra');
	global.quote = require('quote');

	var requestedTaskName = config.requestedTaskName || argv._[0];
	if (requestedTaskName === 'init') {
		commandInit(log);
		process.exit(0);
	}
	else if (requestedTaskName === 'create-task') {
		commandCreateTask(log);
		process.exit(0);
	}
	else if (requestedTaskName === 'schedule') {
		commandSchedule(log);
		return;
	}

	commandRunTask(log, requestedTaskName);
};

<commit_msg>Fixed a spelling mistake in an error message.<commit_after>'use strict';

var argv = require('yargs').argv;
var conf = require('confucious');
var path = require('path');
var fs = require('fs-extra');
var globby = require('globby');
var chalk = require('chalk');
var validate = require('./validate');
var S = require('string');
var AsciiTable = require('ascii-table');

var workingDirectory = process.cwd();
var buildFilePath = path.join(workingDirectory, "build.js");
var tasksDirectory = path.join(workingDirectory, 'tasks');

//
// task-mule init
//
var commandInit = function (log) {

	if (fs.existsSync(buildFilePath)) {
		log.error("Can't overwrite existing 'build.js'.");
		process.exit(1);
	}

	// Auto create build.js.
	var defaultBuildJs = path.join(__dirname, 'build.js');
	fs.copySync(defaultBuildJs, buildFilePath);
	log.info("Created new 'build.js' at " + buildFilePath);
	process.exit(0);
};

//
// task-mule create-task <task-name>
//
var commandCreateTask = function (log) {

	var newTaskName = argv._[1];
	if (!newTaskName) {
		log.error("Task name not specified.");
		process.exit(1);
	}

	if (!S(newTaskName.toLowerCase()).endsWith(".js")) {
		if (newTaskName[newTaskName.length-1] === '.') {
			// Trim final period.
			newTaskName = newTaskName.substring(0, newTaskName.length-1);
		}

		// Auto add extension.
		newTaskName += ".js";
	}

	var newTaskFilePath = path.join(tasksDirectory, newTaskName);
	if (fs.existsSync(newTaskFilePath)) {
		log.error("Can't create task, file already exists: " + newTaskFilePath);
		process.exit(1);
	}

	var defaultTaskFile = path.join(__dirname, 'default-task.js');
	fs.copySync(defaultTaskFile, newTaskFilePath);
	log.info("Created new task file at " + newTaskFilePath);
};

//
// Init config prior to running or listing tasks.
//
var initConfig = function () {

	var buildConfig = require(buildFilePath)(conf, log, validate);

	var defaultConfigFilePath = path.join(workingDirectory, 'config.json');
	if (fs.existsSync(defaultConfigFilePath)) {

		log.verbose("Loading config from file: " + defaultConfigFilePath);

		conf.pushJsonFile(defaultConfigFilePath);
	}

	conf.pushEnv();
	conf.pushArgv();

	if (config.defaultConfig) {
		conf.push(config.defaultConfig)
	}

	buildConfig.init();
};

//
// task-mule schedule
//
var commandSchedule = function (log) {

	if (!fs.existsSync('schedule.json')) {
		log.error('Expected schedule.json to specify the schedule of tasks.');
		process.exit(1);
	}

	initConfig();

	var taskRunner = require('./task-loader.js')({}, log, validate, conf);

	var schedule = JSON.parse(fs.readFileSync('schedule.json', 'utf8'));

	var taskScheduler = require('task-scheduler');
	taskScheduler.start(schedule, config.schedulerCallbacks);
};

//
// task-mule <task-name>
//
var commandRunTask = function (log, requestedTaskName) {

	if (!fs.existsSync(buildFilePath)) {
		log.error("'build.js' not found, please run task-mule in a directory that has this file.");
		log.info("Run 'task-mule init' to create a default 'build.js'.")
		process.exit(1);
	}

	if (!fs.existsSync(tasksDirectory)) {
		log.error("'tasks' directory doesn't exist.");
		log.info("Run 'task-mule create-task <task-name> to create your first task.");
		process.exit(1);
	}

	initConfig();

	var taskRunner = require('./task-loader.js')({}, log, validate, conf);

	if (requestedTaskName) {
	    return taskRunner.runTask(requestedTaskName, conf)
            .catch(function (err) {

                log.error('Build failed.');

                if (err.message) {
                    log.warn(err.message);
                }

                if (err.stack) {
                    log.warn(err.stack);
                }
                else {
                    log.warn('no stack');
                }
                if (!config.noExit) {
                	process.exit(1);
                }

                buildConfig.done();

                throw err;
            })
	        .then(function () {
        		buildConfig.done();
	        });
	}
	else if (argv.tasks) {
	    return taskRunner.resolveAllDependencies(conf)
	    	.then(function () {
			    taskRunner.listTasks();
			    process.exit(1);
			});
	}
	else {
	    log.info("Usage: task-mule <task-name> [options]\n");

	    var optionsTable = new AsciiTable('Options');
	    optionsTable
	      .setHeading('Options', 'Description');

	    buildConfig.options.forEach(function (option) {
	        optionsTable.addRow(option[0], option[1]);
	    });

	    console.log(chalk.bold.green(optionsTable.toString()));

	    var examplesTable = new AsciiTable('Examples');
	    examplesTable.setHeading('What?', 'Command Line');

	     buildConfig.examples.forEach(function (example) {
	     	examplesTable.addRow(example[0], example[1]);
	     });

	    console.log(chalk.bold.green(examplesTable.toString()));

	    process.exit(1);
	}
};

module.exports = function (config) {

	config = config || {};

	var log = config.log || require('./log')(argv.verbose, argv.nocolors);

	global.runCmd = require('./run-cmd')(log);
	global.path = require('path');
	global.fs = require('fs-extra');
	global.quote = require('quote');

	var requestedTaskName = config.requestedTaskName || argv._[0];
	if (requestedTaskName === 'init') {
		commandInit(log);
		process.exit(0);
	}
	else if (requestedTaskName === 'create-task') {
		commandCreateTask(log);
		process.exit(0);
	}
	else if (requestedTaskName === 'schedule') {
		commandSchedule(log);
		return;
	}

	commandRunTask(log, requestedTaskName);
};

<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('canonical-path');
var gutil = require('gulp-util');
var through = require('through');
var mustache = require('mustache');
var sizeOf = require('image-size');
var mime = require('mime');
var md5 = require('md5');
var SVGO = require('svgo');
var svgo = new SVGO();
var appRoot = require('app-root-path').path;

module.exports = function (options) {
    var images = [];
    //var images_path = options.images_path || '';

    if (!options) options = {};

    if(!options.template)
        options.template = path.join(__dirname, 'compass-imagehelper.mustache');

    if(!options.targetFile)
        options.targetFile = '_compass-imagehelper.scss';

    if(!options.prefix)
        options.prefix = '';

    var template = fs.readFileSync(options.template).toString();

    var pathPrefix = function () {
        var result = '';
        if (options.http_images_path) {
            result = options.http_images_path;
        } else if (options.css_path && options.images_path) {
            // relative path from css folder to images
            result = path.relative(options.css_path, options.images_path);
        } else if(options.images_path) {
            // relative from project url
            result = path.relative(appRoot, options.images_path);
        }

        // make sure pathPrefix ends with a trailing slash
        if (result && result.substr(-1) != '/') {
            result = result + '/';
        }
        return result;
    };

    var bufferContents = function (file) {
        if(!options.images_path) {
            // autodetect images_path with the first file
            options.images_path = path.relative( appRoot,  file.base);
        }

        var imageInfo = {};
        var data;
        var encoding;

        var mimetype = mime.lookup(file.path);
        var dimensions;

        if (mimetype == 'image/svg+xml') {
            //data = encodeURIComponent(file.contents.toString());
            //encoding = 'utf8';
            data = file.contents.toString('base64');
            encoding = 'base64';
            try{
                dimensions = sizeOf(file.path);
            } catch(e){
                // could not read width/height from svg. Try again with the slower svgo parser:
                svgo.optimize(file.contents.toString(), function(res) {
                    dimensions = {
                        width: res.info.width,
                        height: res.info.height
                    };
                    console.log(res);
                });
            }

        } else {
            data = file.contents.toString('base64');
            encoding = 'base64';
            dimensions= sizeOf(file.path);
        }
        imageInfo.width = dimensions.width;
        imageInfo.height = dimensions.height;
        imageInfo.mime = mimetype;
        imageInfo.filename = path.basename(file.path);
        imageInfo.basename = path.basename(file.path, path.extname(file.path));
        imageInfo.dirname = path.basename(path.dirname(file.path));
        imageInfo.ext = path.extname(file.path);
        imageInfo.path = path.relative(options.images_path, file.path);
        imageInfo.fullname = imageInfo.path.split('/').join('-').path.split('\\').join('-').replace('.', '-');
        imageInfo.hash = md5(file.contents);
        imageInfo.data = 'url(data:' + mimetype + ';' + encoding + ',' + data + ')';
        images.push(imageInfo);

    };

    var endStream = function () {
        this.emit('data', new gutil.File({
            contents: new Buffer(mustache.render(template, {
                prefix: options.prefix,
                path_prefix: pathPrefix(),
                items: images
            }), 'utf8'),
            path: options.targetFile
        }));
        this.emit('end');
    };

    return new through(bufferContents, endStream);
};
<commit_msg>Fix and simplify imageInfo.fullname<commit_after>var fs = require('fs');
var path = require('canonical-path');
var gutil = require('gulp-util');
var through = require('through');
var mustache = require('mustache');
var sizeOf = require('image-size');
var mime = require('mime');
var md5 = require('md5');
var SVGO = require('svgo');
var svgo = new SVGO();
var appRoot = require('app-root-path').path;

module.exports = function (options) {
    var images = [];
    //var images_path = options.images_path || '';

    if (!options) options = {};

    if(!options.template)
        options.template = path.join(__dirname, 'compass-imagehelper.mustache');

    if(!options.targetFile)
        options.targetFile = '_compass-imagehelper.scss';

    if(!options.prefix)
        options.prefix = '';

    var template = fs.readFileSync(options.template).toString();

    var pathPrefix = function () {
        var result = '';
        if (options.http_images_path) {
            result = options.http_images_path;
        } else if (options.css_path && options.images_path) {
            // relative path from css folder to images
            result = path.relative(options.css_path, options.images_path);
        } else if(options.images_path) {
            // relative from project url
            result = path.relative(appRoot, options.images_path);
        }

        // make sure pathPrefix ends with a trailing slash
        if (result && result.substr(-1) != '/') {
            result = result + '/';
        }
        return result;
    };

    var bufferContents = function (file) {
        if(!options.images_path) {
            // autodetect images_path with the first file
            options.images_path = path.relative( appRoot,  file.base);
        }

        var imageInfo = {};
        var data;
        var encoding;

        var mimetype = mime.lookup(file.path);
        var dimensions;

        if (mimetype == 'image/svg+xml') {
            //data = encodeURIComponent(file.contents.toString());
            //encoding = 'utf8';
            data = file.contents.toString('base64');
            encoding = 'base64';
            try{
                dimensions = sizeOf(file.path);
            } catch(e){
                // could not read width/height from svg. Try again with the slower svgo parser:
                svgo.optimize(file.contents.toString(), function(res) {
                    dimensions = {
                        width: res.info.width,
                        height: res.info.height
                    };
                    console.log(res);
                });
            }

        } else {
            data = file.contents.toString('base64');
            encoding = 'base64';
            dimensions= sizeOf(file.path);
        }
        imageInfo.width = dimensions.width;
        imageInfo.height = dimensions.height;
        imageInfo.mime = mimetype;
        imageInfo.filename = path.basename(file.path);
        imageInfo.basename = path.basename(file.path, path.extname(file.path));
        imageInfo.dirname = path.basename(path.dirname(file.path));
        imageInfo.ext = path.extname(file.path);
        imageInfo.path = path.relative(options.images_path, file.path);
        // Replace /, \ and . with -
        imageInfo.fullname = imageInfo.path.replace(/[\/\\\.]/g, '-');
        imageInfo.hash = md5(file.contents);
        imageInfo.data = 'url(data:' + mimetype + ';' + encoding + ',' + data + ')';
        images.push(imageInfo);

    };

    var endStream = function () {
        this.emit('data', new gutil.File({
            contents: new Buffer(mustache.render(template, {
                prefix: options.prefix,
                path_prefix: pathPrefix(),
                items: images
            }), 'utf8'),
            path: options.targetFile
        }));
        this.emit('end');
    };

    return new through(bufferContents, endStream);
};
<|endoftext|>