<commit_before>'use strict';

var React = require('./util/React');
var t = require('tcomb-validation');
var humanize = require('./util/humanize');
var merge = require('./util/merge');
var getTypeInfo = require('./util/getTypeInfo');
var getOptionsOfEnum = require('./util/getOptionsOfEnum');

var SOURCE = 'tcomb-form-native';
var nooptions = Object.freeze({});
var noop = function () {};

function getComponent(type, options) {
  if (options.factory) {
    return options.factory;
  }
  var name = t.getTypeName(type);
  switch (type.meta.kind) {
    case 'irreducible' :
      return type === t.Bool ? Checkbox :
        type === t.Dat ? DatePicker : Textbox;
    case 'struct' :
      return Struct;
    case 'enums' :
      return Select;
    case 'phoneNumber':
      return PhoneNumber;
    case 'maybe' :
    case 'subtype' :
      return getComponent(type.meta.type, options);
    default :
      t.fail(`[${SOURCE}] unsupported type ${name}`);
  }
}

function getDefaultLabel(ctx, typeInfo) {
  return ctx.label + (typeInfo.isMaybe ? ctx.i18n.optional : '');
}

function sortByText(a, b) {
  return a.text < b.text ? -1 : a.text > b.text ? 1 : 0;
}

function getComparator(order) {
  return {
    asc: sortByText,
    desc: (a, b) => -sortByText(a, b)
  }[order];
}

class Component extends React.Component {

  constructor(props) {
    super(props);
    this.typeInfo = getTypeInfo(props.type);
    this.state = {
      hasError: false,
      value: this.getTransformer().format(props.value)
    };
  }

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    return Form.transformers.defaultTransformer;
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.value !== this.state.value ||
      nextState.hasError !== this.state.hasError ||
      nextProps.value !== this.props.value ||
      nextProps.options !== this.props.options ||
      nextProps.type !== this.props.type;
  }

  componentWillReceiveProps(props) {
    var value = this.getTransformer().format(props.value);
    if (props.type !== this.props.type) {
      this.typeInfo = getTypeInfo(props.type);
    }

    if (value) {
      this.setState({value});
    }
  }

  onChange(value) {
    this.setState({value}, function () {
      this.props.onChange(value, this.props.ctx.path);
    }.bind(this));
  }

  getValue() {
    var result = this.validate();
    this.setState({hasError: !result.isValid()});
    return result;
  }

  validate() {
    var value = this.getTransformer().parse(this.state.value);
    return t.validate(value, this.props.type, this.props.ctx.path);
  }

  getLocals() {
    t.fail(`[${SOURCE}] unimplemented method getLocals() of component ${this.constructor.name}`);
  }

  render() {
    var locals = this.getLocals();
    return locals.template(locals);
  }

}

class Textbox extends Component {

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    if (this.typeInfo.innerType === t.Num) {
      return Form.transformers.defaultNumberTransformer;
    }
    return Form.transformers.defaultTextboxTransformer;
  }

  getLocals() {

    var { ctx, options } = this.props;

    var defaultLabel = getDefaultLabel(ctx, this.typeInfo);
    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = defaultLabel;
    }
    var placeholder = options.placeholder;
    if (!label && !placeholder && ctx.auto === 'placeholders') {
      placeholder = defaultLabel;
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.textbox;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      placeholder: placeholder,
      stylesheet,
      template,
      value
    };

    [
      'help',
      'autoCapitalize',
      'autoCorrect',
      'autoFocus',
      'bufferDelay',
      'clearButtonMode',
      'editable',
      'enablesReturnKeyAutomatically',
      'keyboardType',
      'multiline',
      'onBlur',
      'onEndEditing',
      'onFocus',
      'onSubmitEditing',
      'password',
      'placeholderTextColor',
      'returnKeyType',
      'selectTextOnFocus',
      'secureTextEntry',
      'selectionState'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Checkbox extends Component {

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    return Form.transformers.defaultCheckboxTransformer;
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.checkbox;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help',
      'disabled',
      'onTintColor',
      'thumbTintColor',
      'tintColor'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Select extends Component {

  getNullOption() {
    return this.props.options.nullOption || {value: '', text: '-'};
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.select;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var items = options.options ? options.options.slice() : getOptionsOfEnum(this.typeInfo.innerType);
    if (options.order) {
      items.sort(getComparator(options.order));
    }
    var nullOption = this.getNullOption();
    if (options.nullOption !== false) {
      if (t.Nil.is(value)) { value = nullOption.value; }
      items.unshift(nullOption);
    }

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      options: items,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class DatePicker extends Component {

  constructor(props) {
    super(props);
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;

    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.datepicker;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help',
      'maximumDate',
      'minimumDate',
      'minuteInterval',
      'mode',
      'timeZoneOffsetInMinutes'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Struct extends Component {

  constructor(props) {
    super(props);
  }

  validate() {

    var value = {};
    var errors = [];
    var hasError = false;
    var result;

    for (var ref in this.refs) {
      if (this.refs.hasOwnProperty(ref)) {
        result = this.refs[ref].getValue();
        errors = errors.concat(result.errors);
        value[ref] = result.value;
      }
    }

    if (errors.length === 0) {
      value = new this.typeInfo.innerType(value);
      if (this.typeInfo.isSubtype && errors.length === 0) {
        result = t.validate(value, this.props.type, this.props.ctx.path);
        hasError = !result.isValid();
        errors = errors.concat(result.errors);
      }
    }

    return new t.ValidationResult({errors, value});
  }

  onChange(fieldName, fieldValue, path) {
    var value = t.mixin({}, this.state.value);
    value[fieldName] = fieldValue;
    this.setState({value}, function () {
      this.props.onChange(value, path);
    }.bind(this));
  }

  getLocals() {

    var { ctx, options } = this.props;

    var props = this.typeInfo.innerType.meta.props;
    var order = options.order || Object.keys(props);
    var auto = options.auto || ctx.auto;
    var i18n =  options.i18n || ctx.i18n;
    var label = options.label;
    if (!label && auto === 'labels' && ctx.label) {
      label = ctx.label + (this.typeInfo.isMaybe ? i18n.optional : '');
    }
    var value = this.state.value || {};
    var templates = merge(ctx.templates, options.templates);
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var inputs = {};
    for (var prop in props) {
      if (props.hasOwnProperty(prop)) {
        var propType = props[prop];
        var propOptions = options.fields && options.fields[prop] ? options.fields[prop] : nooptions;
        inputs[prop] = React.createElement(getComponent(propType, propOptions), {
          key: prop,
          ref: prop,
          type: propType,
          options: propOptions,
          value: value[prop],
          onChange: this.onChange.bind(this, prop),
          ctx: {
            auto,
            label: humanize(prop),
            i18n,
            stylesheet,
            templates,
            path: this.props.ctx.path.concat(prop)
          }
        });
      }
    }

    return {
      order,
      label,
      inputs,
      stylesheet,
      template: templates.struct
    };
  }

}

class Form {

  getValue(raw) {
    var result = this.refs.input.getValue();
    if (raw === true) { return result; }
    if (result.isValid()) { return result.value; }
    return null;
  }

  render() {

    var type = this.props.type;
    var options = this.props.options || nooptions;
    var stylesheet = Form.stylesheet;
    var templates = Form.templates;
    var i18n = Form.i18n;

    t.assert(t.Type.is(type), `[${SOURCE}] missing required prop type`);
    t.assert(t.Obj.is(options), `[${SOURCE}] prop options must be an object`);
    t.assert(t.Obj.is(stylesheet), `[${SOURCE}] missing stylesheet config`);
    t.assert(t.Obj.is(templates), `[${SOURCE}] missing templates config`);
    t.assert(t.Obj.is(i18n), `[${SOURCE}] missing i18n config`);

    var Component = getComponent(type, options);

    return React.createElement(Component, {
      ref: 'input',
      type: type,
      options: options,
      value: this.props.value,
      onChange: this.props.onChange || noop,
      ctx: {
        auto: 'labels',
        stylesheet,
        templates,
        i18n,
        path: []
      }
    });
  }

}

class PhoneNumber extends Textbox {
  getLocals() {
    var { ctx, options } = this.props;

    var defaultLabel = getDefaultLabel(ctx, this.typeInfo);
    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = defaultLabel;
    }
    var placeholder = options.placeholder;
    if (!label && !placeholder && ctx.auto === 'placeholders') {
      placeholder = defaultLabel;
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.phoneNumber;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      placeholder: placeholder,
      stylesheet,
      template,
      value
    };

    [
      'help',
      'autoCapitalize',
      'autoCorrect',
      'autoFocus',
      'bufferDelay',
      'clearButtonMode',
      'editable',
      'enablesReturnKeyAutomatically',
      'keyboardType',
      'multiline',
      'onBlur',
      'onEndEditing',
      'onFocus',
      'onSubmitEditing',
      'password',
      'placeholderTextColor',
      'returnKeyType',
      'selectTextOnFocus',
      'secureTextEntry',
      'selectionState'
    ].forEach((name) => locals[name] = options[name]);
  }
}

Form.transformers = {

  defaultTransformer: {
    format: value => t.Nil.is(value) ? null : value,
    parse: value => value
  },

  defaultNumberTransformer: {
    format: value => t.Nil.is(value) ? value : String(value),
    parse: value => {
      var n = parseFloat(value);
      var isNumeric = (value - n + 1) >= 0;
      return isNumeric ? n : value;
    }
  },

  defaultTextboxTransformer: {
    format: value => t.Nil.is(value) ? null : value,
    parse: value => (t.Str.is(value) && value.trim() === '') || t.Nil.is(value) ? null : value
  },

  defaultCheckboxTransformer: {
    format: value => t.Nil.is(value) ? false : value,
    parse: value => t.Nil.is(value) ? false : value
  }

};

module.exports = {
  Component,
  Textbox,
  Checkbox,
  Select,
  DatePicker,
  Struct,
  PhoneNumber,
  Form,
};<commit_msg>updating phone number class<commit_after>'use strict';

var React = require('./util/React');
var t = require('tcomb-validation');
var humanize = require('./util/humanize');
var merge = require('./util/merge');
var getTypeInfo = require('./util/getTypeInfo');
var getOptionsOfEnum = require('./util/getOptionsOfEnum');

var SOURCE = 'tcomb-form-native';
var nooptions = Object.freeze({});
var noop = function () {};

function getComponent(type, options) {
  if (options.factory) {
    return options.factory;
  }
  var name = t.getTypeName(type);
  switch (type.meta.kind) {
    case 'irreducible' :
      return type === t.Bool ? Checkbox :
        type === t.Dat ? DatePicker : Textbox;
    case 'struct' :
      return Struct;
    case 'enums' :
      return Select;
    case 'phoneNumber':
      return PhoneNumber;
    case 'maybe' :
    case 'subtype' :
      return getComponent(type.meta.type, options);
    default :
      t.fail(`[${SOURCE}] unsupported type ${name}`);
  }
}

function getDefaultLabel(ctx, typeInfo) {
  return ctx.label + (typeInfo.isMaybe ? ctx.i18n.optional : '');
}

function sortByText(a, b) {
  return a.text < b.text ? -1 : a.text > b.text ? 1 : 0;
}

function getComparator(order) {
  return {
    asc: sortByText,
    desc: (a, b) => -sortByText(a, b)
  }[order];
}

class Component extends React.Component {

  constructor(props) {
    super(props);
    this.typeInfo = getTypeInfo(props.type);
    this.state = {
      hasError: false,
      value: this.getTransformer().format(props.value)
    };
  }

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    return Form.transformers.defaultTransformer;
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.value !== this.state.value ||
      nextState.hasError !== this.state.hasError ||
      nextProps.value !== this.props.value ||
      nextProps.options !== this.props.options ||
      nextProps.type !== this.props.type;
  }

  componentWillReceiveProps(props) {
    var value = this.getTransformer().format(props.value);
    if (props.type !== this.props.type) {
      this.typeInfo = getTypeInfo(props.type);
    }

    if (value) {
      this.setState({value});
    }
  }

  onChange(value) {
    this.setState({value}, function () {
      this.props.onChange(value, this.props.ctx.path);
    }.bind(this));
  }

  getValue() {
    var result = this.validate();
    this.setState({hasError: !result.isValid()});
    return result;
  }

  validate() {
    var value = this.getTransformer().parse(this.state.value);
    return t.validate(value, this.props.type, this.props.ctx.path);
  }

  getLocals() {
    t.fail(`[${SOURCE}] unimplemented method getLocals() of component ${this.constructor.name}`);
  }

  render() {
    var locals = this.getLocals();
    return locals.template(locals);
  }

}

class Textbox extends Component {

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    if (this.typeInfo.innerType === t.Num) {
      return Form.transformers.defaultNumberTransformer;
    }
    return Form.transformers.defaultTextboxTransformer;
  }

  getLocals() {

    var { ctx, options } = this.props;

    var defaultLabel = getDefaultLabel(ctx, this.typeInfo);
    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = defaultLabel;
    }
    var placeholder = options.placeholder;
    if (!label && !placeholder && ctx.auto === 'placeholders') {
      placeholder = defaultLabel;
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.textbox;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      placeholder: placeholder,
      stylesheet,
      template,
      value
    };

    [
      'help',
      'autoCapitalize',
      'autoCorrect',
      'autoFocus',
      'bufferDelay',
      'clearButtonMode',
      'editable',
      'enablesReturnKeyAutomatically',
      'keyboardType',
      'multiline',
      'onBlur',
      'onEndEditing',
      'onFocus',
      'onSubmitEditing',
      'password',
      'placeholderTextColor',
      'returnKeyType',
      'selectTextOnFocus',
      'secureTextEntry',
      'selectionState'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Checkbox extends Component {

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    return Form.transformers.defaultCheckboxTransformer;
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.checkbox;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help',
      'disabled',
      'onTintColor',
      'thumbTintColor',
      'tintColor'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Select extends Component {

  getNullOption() {
    return this.props.options.nullOption || {value: '', text: '-'};
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.select;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var items = options.options ? options.options.slice() : getOptionsOfEnum(this.typeInfo.innerType);
    if (options.order) {
      items.sort(getComparator(options.order));
    }
    var nullOption = this.getNullOption();
    if (options.nullOption !== false) {
      if (t.Nil.is(value)) { value = nullOption.value; }
      items.unshift(nullOption);
    }

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      options: items,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class DatePicker extends Component {

  constructor(props) {
    super(props);
  }

  getLocals() {

    var { ctx, options } = this.props;

    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = getDefaultLabel(ctx, this.typeInfo);
    }
    var value = this.state.value;

    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.datepicker;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      stylesheet,
      template,
      value
    };

    [
      'help',
      'maximumDate',
      'minimumDate',
      'minuteInterval',
      'mode',
      'timeZoneOffsetInMinutes'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

class Struct extends Component {

  constructor(props) {
    super(props);
  }

  validate() {

    var value = {};
    var errors = [];
    var hasError = false;
    var result;

    for (var ref in this.refs) {
      if (this.refs.hasOwnProperty(ref)) {
        result = this.refs[ref].getValue();
        errors = errors.concat(result.errors);
        value[ref] = result.value;
      }
    }

    if (errors.length === 0) {
      value = new this.typeInfo.innerType(value);
      if (this.typeInfo.isSubtype && errors.length === 0) {
        result = t.validate(value, this.props.type, this.props.ctx.path);
        hasError = !result.isValid();
        errors = errors.concat(result.errors);
      }
    }

    return new t.ValidationResult({errors, value});
  }

  onChange(fieldName, fieldValue, path) {
    var value = t.mixin({}, this.state.value);
    value[fieldName] = fieldValue;
    this.setState({value}, function () {
      this.props.onChange(value, path);
    }.bind(this));
  }

  getLocals() {

    var { ctx, options } = this.props;

    var props = this.typeInfo.innerType.meta.props;
    var order = options.order || Object.keys(props);
    var auto = options.auto || ctx.auto;
    var i18n =  options.i18n || ctx.i18n;
    var label = options.label;
    if (!label && auto === 'labels' && ctx.label) {
      label = ctx.label + (this.typeInfo.isMaybe ? i18n.optional : '');
    }
    var value = this.state.value || {};
    var templates = merge(ctx.templates, options.templates);
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var inputs = {};
    for (var prop in props) {
      if (props.hasOwnProperty(prop)) {
        var propType = props[prop];
        var propOptions = options.fields && options.fields[prop] ? options.fields[prop] : nooptions;
        inputs[prop] = React.createElement(getComponent(propType, propOptions), {
          key: prop,
          ref: prop,
          type: propType,
          options: propOptions,
          value: value[prop],
          onChange: this.onChange.bind(this, prop),
          ctx: {
            auto,
            label: humanize(prop),
            i18n,
            stylesheet,
            templates,
            path: this.props.ctx.path.concat(prop)
          }
        });
      }
    }

    return {
      order,
      label,
      inputs,
      stylesheet,
      template: templates.struct
    };
  }

}

class Form {

  getValue(raw) {
    var result = this.refs.input.getValue();
    if (raw === true) { return result; }
    if (result.isValid()) { return result.value; }
    return null;
  }

  render() {

    var type = this.props.type;
    var options = this.props.options || nooptions;
    var stylesheet = Form.stylesheet;
    var templates = Form.templates;
    var i18n = Form.i18n;

    t.assert(t.Type.is(type), `[${SOURCE}] missing required prop type`);
    t.assert(t.Obj.is(options), `[${SOURCE}] prop options must be an object`);
    t.assert(t.Obj.is(stylesheet), `[${SOURCE}] missing stylesheet config`);
    t.assert(t.Obj.is(templates), `[${SOURCE}] missing templates config`);
    t.assert(t.Obj.is(i18n), `[${SOURCE}] missing i18n config`);

    var Component = getComponent(type, options);

    return React.createElement(Component, {
      ref: 'input',
      type: type,
      options: options,
      value: this.props.value,
      onChange: this.props.onChange || noop,
      ctx: {
        auto: 'labels',
        stylesheet,
        templates,
        i18n,
        path: []
      }
    });
  }

}

class PhoneNumber extends Component {

  getTransformer() {
    if (this.props.options.transformer) {
      return this.props.options.transformer;
    }
    if (this.typeInfo.innerType === t.Num) {
      return Form.transformers.defaultNumberTransformer;
    }
    return Form.transformers.defaultTextboxTransformer;
  }

  getLocals() {

    var { ctx, options } = this.props;

    var defaultLabel = getDefaultLabel(ctx, this.typeInfo);
    var label = options.label;
    if (!label && ctx.auto === 'labels' && ctx.label) {
      label = defaultLabel;
    }
    var placeholder = options.placeholder;
    if (!label && !placeholder && ctx.auto === 'placeholders') {
      placeholder = defaultLabel;
    }
    var value = this.state.value;
    var error = t.Func.is(options.error) ? options.error(value) : options.error;
    var template = options.template || ctx.templates.phoneNumber;
    var stylesheet = options.stylesheet || ctx.stylesheet;

    var locals = {
      error,
      hasError: options.hasError || this.state.hasError,
      label,
      onChange: this.onChange.bind(this),
      placeholder: placeholder,
      stylesheet,
      template,
      value
    };

    [
      'help',
      'autoCapitalize',
      'autoCorrect',
      'autoFocus',
      'bufferDelay',
      'clearButtonMode',
      'editable',
      'enablesReturnKeyAutomatically',
      'keyboardType',
      'multiline',
      'onBlur',
      'onEndEditing',
      'onFocus',
      'onSubmitEditing',
      'password',
      'placeholderTextColor',
      'returnKeyType',
      'selectTextOnFocus',
      'secureTextEntry',
      'selectionState'
    ].forEach((name) => locals[name] = options[name]);

    return locals;
  }

}

Form.transformers = {

  defaultTransformer: {
    format: value => t.Nil.is(value) ? null : value,
    parse: value => value
  },

  defaultNumberTransformer: {
    format: value => t.Nil.is(value) ? value : String(value),
    parse: value => {
      var n = parseFloat(value);
      var isNumeric = (value - n + 1) >= 0;
      return isNumeric ? n : value;
    }
  },

  defaultTextboxTransformer: {
    format: value => t.Nil.is(value) ? null : value,
    parse: value => (t.Str.is(value) && value.trim() === '') || t.Nil.is(value) ? null : value
  },

  defaultCheckboxTransformer: {
    format: value => t.Nil.is(value) ? false : value,
    parse: value => t.Nil.is(value) ? false : value
  }

};

module.exports = {
  Component,
  Textbox,
  Checkbox,
  Select,
  DatePicker,
  Struct,
  PhoneNumber,
  Form,
};<|endoftext|>


<commit_before>/*
 * request.js
 * @flow
 */
/* globals $TypedArray:false */

import _ from "lodash";
import pako from "pako";
import Toast from "libs/toast";

type RequestOptions = {
  headers?: { [key: string]: string };
  method?: 'GET' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PUT' | 'PATCH';
  timeout?: number;
};

type RequestOptionsWithData<T> = {
  data: T;
  headers?: { [key: string]: string };
  method?: 'GET' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PUT' | 'PATCH';
  timeout?: number;
};

class Request {

  // IN:  nothing
  // OUT: json
  receiveJSON = (url: string, options: RequestOptions = {}): Promise<any> =>
    this.triggerRequest(
      url,
      _.defaultsDeep(options, { headers: { Accept: "application/json" } }),
      this.handleEmptyJsonResponse,
    );

  prepareJSON = (url: string, options: RequestOptionsWithData<any>): RequestOptions => {
    // Sanity check
    // Requests without body should not send 'json' header and use 'receiveJSON' instead
    if (!options.data) {
      if (options.method === "POST" || options.method === "PUT") {
        console.warn("Sending POST/PUT request without body", url);
      }
      return options;
    }

    let body = _.isString(options.data) ?
        options.data :
        JSON.stringify(options.data);

    if (options.compress) {
      body = pako.gzip(body);
      if (options.headers == null) {
        options.headers = {
          "Content-Encoding": "gzip",
        };
      } else {
        options.headers["Content-Encoding"] = "gzip";
      }
    }

    return _.defaultsDeep(options, {
      method: "POST",
      body,
      headers: {
        "Content-Type": "application/json",
      },
    });
  }

  // IN:  json
  // OUT: json
  sendJSONReceiveJSON = (url: string, options: RequestOptionsWithData<any>): Promise<any> =>
    this.receiveJSON(url, this.prepareJSON(url, options));

  // IN:  multipart formdata
  // OUT: json
  sendMultipartFormReceiveJSON = (url: string, options: RequestOptionsWithData<FormData | Object>): Promise<any> => {
    function toFormData(input: { [key: string]: Array<string> | File | Object | string },
      form: ?FormData = null, namespace: ?string = null): FormData {
      let formData;
      if (form != null) {
        formData = form;
      } else {
        formData = new FormData();
      }

      for (const key of Object.keys(input)) {
        let formKey;
        const value = input[key];
        if (namespace != null) {
          formKey = `${namespace}[${key}]`;
        } else {
          formKey = key;
        }

        if (Array.isArray(value)) {
          for (const val of value) {
            formData.append(`${formKey}[]`, val);
          }
        } else if (value instanceof File) {
          formData.append(`${formKey}[]`, value, value.name);
        } else if (typeof value === "string") {
          formData.append(formKey, value);
        } else { // nested object
          toFormData(value, formData, key);
        }
      }
      return formData;
    }

    const body = options.data instanceof FormData ?
        options.data :
        toFormData(options.data);

    return this.receiveJSON(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body,
      }),
    );
  }

  // IN:  url-encoded formdata
  // OUT: json
  sendUrlEncodedFormReceiveJSON = (url: string, options: RequestOptionsWithData<string>): Promise<any> =>
    this.receiveJSON(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body: options.data,
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }),
    )

  receiveArraybuffer = (url: string, options: RequestOptions = {}): Promise<ArrayBuffer> =>
    this.triggerRequest(
      url,
      _.defaultsDeep(options, { headers: { Accept: "application/octet-stream" } }),
      response => response.arrayBuffer())

  // IN:  arraybuffer
  // OUT: arraybuffer
  sendArraybufferReceiveArraybuffer = (url: string, options: RequestOptionsWithData<ArrayBuffer | $TypedArray>): Promise<ArrayBuffer> => {
    let body = options.data instanceof ArrayBuffer ?
        options.data :
        options.data.buffer.slice(0, options.data.byteLength);

    if (options.compress) {
      body = pako.gzip(body);
      if (options.headers == null) {
        options.headers = {
          "Content-Encoding": "gzip",
        };
      } else {
        options.headers["Content-Encoding"] = "gzip";
      }
    }

    return this.receiveArraybuffer(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body,
        headers: {
          "Content-Type": "application/octet-stream",
        },
      }),
    );
  }

  // IN:  JSON
  // OUT: arraybuffer
  sendJSONReceiveArraybuffer = (url: string, options: RequestOptionsWithData<any>): Promise<ArrayBuffer> =>
    this.receiveArraybuffer(url, this.prepareJSON(url, options));

  // TODO: babel doesn't support generic arrow-functions yet
  triggerRequest<T>(url: string, options: RequestOptions | RequestOptionsWithData<T> = {}, responseDataHandler: ?Function = null): Promise<*> {
    const defaultOptions = {
      method: "GET",
      host: "",
      credentials: "same-origin",
      headers: {},
      doNotCatch: false,
      params: null,
    };

    options = _.defaultsDeep(options, defaultOptions);

    if (options.host) {
      url = options.host + url;
    }

    // Append URL parameters to the URL
    if (options.params) {
      let appendix;
      const { params } = options;

      if (_.isString(params)) {
        appendix = params;
      } else if (_.isObject(params)) {
        appendix = _.map(params, (value: string, key: string) => `${key}=${value}`).join("&");
      } else {
        throw new Error("options.params is expected to be a string or object for a request!");
      }

      url += `?${appendix}`;
    }

    const headers = new Headers();
    for (const name of Object.keys(options.headers)) {
      headers.set(name, options.headers[name]);
    }
    options.headers = headers;

    let fetchPromise = fetch(url, options).then(this.handleStatus);
    if (responseDataHandler != null) {
      fetchPromise = fetchPromise.then(responseDataHandler);
    }

    if (!options.doNotCatch) {
      fetchPromise = fetchPromise.catch(this.handleError);
    }

    if (options.timeout != null) {
      return Promise.race([fetchPromise, this.timeoutPromise(options.timeout)])
        .then((result) => {
          if (result === "timeout") {
            throw new Error("Timeout");
          } else {
            return result;
          }
        });
    } else {
      return fetchPromise;
    }
  }

  timeoutPromise = (timeout: number): Promise<string> =>
    new Promise((resolve) => {
      setTimeout(
        () => resolve("timeout"),
        timeout,
      );
    });

  handleStatus = (response: Response): Promise<Response> => {
    if (response.status >= 200 && response.status < 400) {
      return Promise.resolve(response);
    }
    return Promise.reject(response);
  }

  handleError = (error: Response | Error): Promise<void> => {
    if (error instanceof Response) {
      return error.text().then(
        (text) => {
          try {
            const json = JSON.parse(text);

            // Propagate HTTP status code for further processing down the road
            if (error.status) {
              json.status = error.status;
            }

            Toast.message(json.messages);
            return Promise.reject(json);
          } catch (jsonError) {
            Toast.error(text);
            return Promise.reject(text);
          }
        },
        (textError) => {
          Toast.error(textError.toString());
          return Promise.reject(textError);
        });
    } else {
      console.error(error);
      return Promise.reject(error);
    }
  }

  handleEmptyJsonResponse = (response: Response): Promise<{}> => {
    return response.text().then(responseText => {
      if (responseText.length === 0) {
        return {};
      } else {
        return JSON.parse(responseText);
      }
    })
  }
}

export default new Request();
<commit_msg>fix linting<commit_after>/*
 * request.js
 * @flow
 */
/* globals $TypedArray:false */

import _ from "lodash";
import pako from "pako";
import Toast from "libs/toast";

type RequestOptions = {
  headers?: { [key: string]: string };
  method?: 'GET' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PUT' | 'PATCH';
  timeout?: number;
};

type RequestOptionsWithData<T> = {
  data: T;
  headers?: { [key: string]: string };
  method?: 'GET' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PUT' | 'PATCH';
  timeout?: number;
};

class Request {

  // IN:  nothing
  // OUT: json
  receiveJSON = (url: string, options: RequestOptions = {}): Promise<any> =>
    this.triggerRequest(
      url,
      _.defaultsDeep(options, { headers: { Accept: "application/json" } }),
      this.handleEmptyJsonResponse,
    );

  prepareJSON = (url: string, options: RequestOptionsWithData<any>): RequestOptions => {
    // Sanity check
    // Requests without body should not send 'json' header and use 'receiveJSON' instead
    if (!options.data) {
      if (options.method === "POST" || options.method === "PUT") {
        console.warn("Sending POST/PUT request without body", url);
      }
      return options;
    }

    let body = _.isString(options.data) ?
        options.data :
        JSON.stringify(options.data);

    if (options.compress) {
      body = pako.gzip(body);
      if (options.headers == null) {
        options.headers = {
          "Content-Encoding": "gzip",
        };
      } else {
        options.headers["Content-Encoding"] = "gzip";
      }
    }

    return _.defaultsDeep(options, {
      method: "POST",
      body,
      headers: {
        "Content-Type": "application/json",
      },
    });
  }

  // IN:  json
  // OUT: json
  sendJSONReceiveJSON = (url: string, options: RequestOptionsWithData<any>): Promise<any> =>
    this.receiveJSON(url, this.prepareJSON(url, options));

  // IN:  multipart formdata
  // OUT: json
  sendMultipartFormReceiveJSON = (url: string, options: RequestOptionsWithData<FormData | Object>): Promise<any> => {
    function toFormData(input: { [key: string]: Array<string> | File | Object | string },
      form: ?FormData = null, namespace: ?string = null): FormData {
      let formData;
      if (form != null) {
        formData = form;
      } else {
        formData = new FormData();
      }

      for (const key of Object.keys(input)) {
        let formKey;
        const value = input[key];
        if (namespace != null) {
          formKey = `${namespace}[${key}]`;
        } else {
          formKey = key;
        }

        if (Array.isArray(value)) {
          for (const val of value) {
            formData.append(`${formKey}[]`, val);
          }
        } else if (value instanceof File) {
          formData.append(`${formKey}[]`, value, value.name);
        } else if (typeof value === "string") {
          formData.append(formKey, value);
        } else { // nested object
          toFormData(value, formData, key);
        }
      }
      return formData;
    }

    const body = options.data instanceof FormData ?
        options.data :
        toFormData(options.data);

    return this.receiveJSON(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body,
      }),
    );
  }

  // IN:  url-encoded formdata
  // OUT: json
  sendUrlEncodedFormReceiveJSON = (url: string, options: RequestOptionsWithData<string>): Promise<any> =>
    this.receiveJSON(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body: options.data,
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }),
    )

  receiveArraybuffer = (url: string, options: RequestOptions = {}): Promise<ArrayBuffer> =>
    this.triggerRequest(
      url,
      _.defaultsDeep(options, { headers: { Accept: "application/octet-stream" } }),
      response => response.arrayBuffer())

  // IN:  arraybuffer
  // OUT: arraybuffer
  sendArraybufferReceiveArraybuffer = (url: string, options: RequestOptionsWithData<ArrayBuffer | $TypedArray>): Promise<ArrayBuffer> => {
    let body = options.data instanceof ArrayBuffer ?
        options.data :
        options.data.buffer.slice(0, options.data.byteLength);

    if (options.compress) {
      body = pako.gzip(body);
      if (options.headers == null) {
        options.headers = {
          "Content-Encoding": "gzip",
        };
      } else {
        options.headers["Content-Encoding"] = "gzip";
      }
    }

    return this.receiveArraybuffer(
      url,
      _.defaultsDeep(options, {
        method: "POST",
        body,
        headers: {
          "Content-Type": "application/octet-stream",
        },
      }),
    );
  }

  // IN:  JSON
  // OUT: arraybuffer
  sendJSONReceiveArraybuffer = (url: string, options: RequestOptionsWithData<any>): Promise<ArrayBuffer> =>
    this.receiveArraybuffer(url, this.prepareJSON(url, options));

  // TODO: babel doesn't support generic arrow-functions yet
  triggerRequest<T>(url: string, options: RequestOptions | RequestOptionsWithData<T> = {}, responseDataHandler: ?Function = null): Promise<*> {
    const defaultOptions = {
      method: "GET",
      host: "",
      credentials: "same-origin",
      headers: {},
      doNotCatch: false,
      params: null,
    };

    options = _.defaultsDeep(options, defaultOptions);

    if (options.host) {
      url = options.host + url;
    }

    // Append URL parameters to the URL
    if (options.params) {
      let appendix;
      const { params } = options;

      if (_.isString(params)) {
        appendix = params;
      } else if (_.isObject(params)) {
        appendix = _.map(params, (value: string, key: string) => `${key}=${value}`).join("&");
      } else {
        throw new Error("options.params is expected to be a string or object for a request!");
      }

      url += `?${appendix}`;
    }

    const headers = new Headers();
    for (const name of Object.keys(options.headers)) {
      headers.set(name, options.headers[name]);
    }
    options.headers = headers;

    let fetchPromise = fetch(url, options).then(this.handleStatus);
    if (responseDataHandler != null) {
      fetchPromise = fetchPromise.then(responseDataHandler);
    }

    if (!options.doNotCatch) {
      fetchPromise = fetchPromise.catch(this.handleError);
    }

    if (options.timeout != null) {
      return Promise.race([fetchPromise, this.timeoutPromise(options.timeout)])
        .then((result) => {
          if (result === "timeout") {
            throw new Error("Timeout");
          } else {
            return result;
          }
        });
    } else {
      return fetchPromise;
    }
  }

  timeoutPromise = (timeout: number): Promise<string> =>
    new Promise((resolve) => {
      setTimeout(
        () => resolve("timeout"),
        timeout,
      );
    });

  handleStatus = (response: Response): Promise<Response> => {
    if (response.status >= 200 && response.status < 400) {
      return Promise.resolve(response);
    }
    return Promise.reject(response);
  }

  handleError = (error: Response | Error): Promise<void> => {
    if (error instanceof Response) {
      return error.text().then(
        (text) => {
          try {
            const json = JSON.parse(text);

            // Propagate HTTP status code for further processing down the road
            if (error.status) {
              json.status = error.status;
            }

            Toast.message(json.messages);
            return Promise.reject(json);
          } catch (jsonError) {
            Toast.error(text);
            return Promise.reject(text);
          }
        },
        (textError) => {
          Toast.error(textError.toString());
          return Promise.reject(textError);
        });
    } else {
      console.error(error);
      return Promise.reject(error);
    }
  }

  handleEmptyJsonResponse = (response: Response): Promise<{}> =>
    response.text().then(responseText => {
      if (responseText.length === 0) {
        return {};
      } else {
        return JSON.parse(responseText);
      }
    })
}

export default new Request();
<|endoftext|>


<commit_before>ManageIQ.explorer = {};

ManageIQ.explorer.updateElement = function(element, options) {
  if (_.isString(options.legend)) {
    $('#' + element).html(options.legend);
  } else if (_.isString(options.title)) {
    $('#' + element).attr( {'title': options.title});
  } else if (_.isString(options.class)) {
    if (options.add) {
      $('#' + element).addClass(options.class);
    } else {
      $('#' + element).removeClass(options.class);
    }
  }
};

ManageIQ.explorer.buildCalendar = function(options) {
  ManageIQ.calendar.calDateFrom = _.isString(options.dateFrom) ? new Date(options.dateFrom) : undefined;
  ManageIQ.calendar.calDateTo = _.isString(options.dateTo) ? new Date(options.dateTo) : undefined;
  ManageIQ.calendar.calSkipDays = _.isObject(options.skipDays) ? options.skipDays : undefined;

  miqBuildCalendar();
};

ManageIQ.explorer.lock_tree = function(tree_name, lock) {
  var tree = miqTreeObject(tree_name);
  lock ? tree.disableAll({silent: true, keepState: true}) : tree.enableAll();
  miqDimDiv('#' + tree_name + '_div', lock);
};

ManageIQ.explorer.clearSearchToggle = function(show) {
  if (show) {
    $('#clear_search').show();
  } else {
    $('#clear_search').hide();
  }
};

ManageIQ.explorer.process = function(data) {
  switch (data.explorer) {
    case 'flash':
      ManageIQ.explorer.processFlash(data);
      break;
    case 'replace_right_cell':
      ManageIQ.explorer.processReplaceRightCell(data);
      break;
    case 'replace_main_div':
      ManageIQ.explorer.processReplaceMainDiv(data);
      break;
    case 'buttons':
      ManageIQ.explorer.processButtons(data);
      break;
    case 'window':
      ManageIQ.explorer.processWindow(data);
      break;
  }
};

ManageIQ.explorer.processWindow = function(data) {
  if (_.isString(data.openUrl)) {
    window.open(data.openUrl);
  }
  ManageIQ.explorer.spinnerOff(data);
};

ManageIQ.explorer.processButtons = function(data) {
  ManageIQ.explorer.miqButtons(data);
};

ManageIQ.explorer.processReplaceMainDiv = function(data) {
  ManageIQ.explorer.updatePartials(data);
};

ManageIQ.explorer.processFlash = function(data) {
  ManageIQ.explorer.replacePartials(data);
  ManageIQ.explorer.spinnerOff(data);
  ManageIQ.explorer.scrollTop(data);
  ManageIQ.explorer.focus(data);

  if (!_.isUndefined(data.activateNode)) {
    miqTreeActivateNode(data.activateNode.tree, data.activateNode.node);
  }
};

ManageIQ.explorer.replacePartials = function(data) {
  if (_.isObject(data.replacePartials)) {
    _.forEach(data.replacePartials, function (content, element) {
      $('#' + element).replaceWith(content);
    });
  }
};

ManageIQ.explorer.updatePartials = function(data) {
  if (_.isObject(data.updatePartials)) {
    _.forEach(data.updatePartials, function (content, element) {
      $('#' + element).html(content);
    });
  }
};

ManageIQ.explorer.spinnerOff = function(data) {
  if (data.spinnerOff) {
    miqSparkle(false);
  }
};

ManageIQ.explorer.scrollTop = function(data) {
  if (data.scrollTop) {
    $('#main_div').scrollTop(0);
  }
};

ManageIQ.explorer.miqButtons = function(data) {
  miqButtons(data.showMiqButtons ? 'show' : 'hide');
};

ManageIQ.explorer.focus = function(data) {
  if (_.isString(data.focus)) {
    var element = $('#' + data.focus);
    if ( element.length ) element.focus();
  }
}

ManageIQ.explorer.processReplaceRightCell = function(data) {
  /* variables for the expression editor */
  if (_.isObject(data.expEditor)) {
    if (_.isObject(data.expEditor.first)) {
      if (!_.isUndefined(data.expEditor.first.type)) {
        ManageIQ.expEditor.first.type   = data.expEditor.first.type;
      }
      if (!_.isUndefined(data.expEditor.first.title)) {
        ManageIQ.expEditor.first.title  = data.expEditor.first.title;
      }
    }

    if (_.isObject(data.expEditor.second)) {
      if (!_.isUndefined(data.expEditor.second.type)) {
        ManageIQ.expEditor.second.type   = data.expEditor.second.type;
      }
      if (!_.isUndefined(data.expEditor.second.title)) {
        ManageIQ.expEditor.second.title  = data.expEditor.second.title;
      }
    }
  }

  ManageIQ.explorer.miqButtons(data);

  if (_.isString(data.clearTreeCookies)) { miqDeleteTreeCookies(data.clearTreeCookies); }

  if (_.isString(data.accordionSwap)) {
    miqAccordionSwap('#accordion .panel-collapse.collapse.in', '#' + data.accordionSwap + '_accord');
  }

  /* dealing with tree nodes */
  if (!_.isUndefined(data.addNodes)) {

    if (data.addNodes.remove) {
      miqRemoveNodeChildren(data.addNodes.activeTree, data.addNodes.key);
    }

    miqAddNodeChildren(data.addNodes.activeTree,
                       data.addNodes.key,
                       data.addNodes.osf,
                       data.addNodes.nodes);
  }

  if (!_.isUndefined(data.deleteNode)) {
    var del_node = miqTreeFindNodeByKey(data.deleteNode.activeTree, data.deleteNode.node)
    miqTreeObject(data.deleteNode.activeTree).deleteNode(del_node);
  }

  if (_.isString(data.dashboardUrl)) {
    ManageIQ.widget.dashboardUrl = data.dashboardUrl;
  }

  if ($('#advsearchModal').hasClass('modal fade in')) {
    $('#advsearchModal').modal('hide');
  }

  ManageIQ.explorer.updatePartials(data);

  if (_.isObject(data.updateElements)) {
    _.forEach(data.updateElements, function (options, element) {
        ManageIQ.explorer.updateElement(element, options);
    });
  }

  ManageIQ.explorer.replacePartials(data);

  if (_.isObject(data.buildCalendar)) { ManageIQ.explorer.buildCalendar(data.buildCalendar); }

  if (data.initDashboard) { miqInitDashboardCols(); }

  if (data.clearGtlListGrid) { ManageIQ.grids.gtl_list_grid = undefined; }

  if (_.isObject(data.setVisibility))
    _.forEach(data.setVisibility, function (visible, element) {
      if ( miqDomElementExists(element) ) {
        if ( visible ) {
          $('#' + element).show()
        } else {
          $('#' + element).hide()
        }
      }
    });

  ManageIQ.explorer.scrollTop(data);

  if (_.isString(data.rightCellText)) {
    $('h1#explorer_title > span#explorer_title_text')
      .html(data.rightCellText);
  }

  if (_.isArray(data.reloadToolbars)) {
    ManageIQ.angular.rxSubject.onNext({
      redrawToolbar: data.reloadToolbars
    })
  } else if (_.isObject(data.reloadToolbars)) {
    _.forEach(data.reloadToolbars, function (content, element) {
      $('#' + element).html(content);
    });
    miqInitToolbars();
  }

  ManageIQ.record = data.record;

  if (!_.isUndefined(data.activateNode)) {
    miqExpandParentNodes(data.activateNode.activeTree, data.activateNode.osf);
    miqTreeActivateNodeSilently(data.activateNode.activeTree, data.activateNode.osf);
  }

  if (_.isObject(data.lockTrees)) {
    _.forEach(data.lockTrees, function (lock, tree) {
      ManageIQ.explorer.lock_tree(tree, lock);
    });
  }

  if (_.isObject(data.chartData)) {
    ManageIQ.charts.chartData = data.chartData;
    // FIXME:  @out << Charting.js_load_statement(true)
  }

  if (data.resetChanges) { ManageIQ.changes = null; }
  if (data.resetOneTrans) { ManageIQ.oneTransition.oneTrans = 0; }
  if (data.oneTransIE) { ManageIQ.oneTransition.IEButtonPressed = true; }

  ManageIQ.explorer.focus(data);

  if (!_.isUndefined(data.clearSearch)) {
    ManageIQ.explorer.clearSearchToggle(data.clearSearch);
  }

  miqInitMainContent();
  miqInitAccordions();

  if (data.hideModal) { $('#quicksearchbox').modal('hide'); }
  if (data.initAccords) { miqInitAccordions(); }

  if (_.isString(data.ajaxUrl)) {
    miqAsyncAjax(data.ajaxUrl);
  } else {
    miqSparkleOff();
  }

  return null;
};
<commit_msg>Explorer js - reloadToolbars being empty array means don't update toolbars, not remove<commit_after>ManageIQ.explorer = {};

ManageIQ.explorer.updateElement = function(element, options) {
  if (_.isString(options.legend)) {
    $('#' + element).html(options.legend);
  } else if (_.isString(options.title)) {
    $('#' + element).attr( {'title': options.title});
  } else if (_.isString(options.class)) {
    if (options.add) {
      $('#' + element).addClass(options.class);
    } else {
      $('#' + element).removeClass(options.class);
    }
  }
};

ManageIQ.explorer.buildCalendar = function(options) {
  ManageIQ.calendar.calDateFrom = _.isString(options.dateFrom) ? new Date(options.dateFrom) : undefined;
  ManageIQ.calendar.calDateTo = _.isString(options.dateTo) ? new Date(options.dateTo) : undefined;
  ManageIQ.calendar.calSkipDays = _.isObject(options.skipDays) ? options.skipDays : undefined;

  miqBuildCalendar();
};

ManageIQ.explorer.lock_tree = function(tree_name, lock) {
  var tree = miqTreeObject(tree_name);
  lock ? tree.disableAll({silent: true, keepState: true}) : tree.enableAll();
  miqDimDiv('#' + tree_name + '_div', lock);
};

ManageIQ.explorer.clearSearchToggle = function(show) {
  if (show) {
    $('#clear_search').show();
  } else {
    $('#clear_search').hide();
  }
};

ManageIQ.explorer.process = function(data) {
  switch (data.explorer) {
    case 'flash':
      ManageIQ.explorer.processFlash(data);
      break;
    case 'replace_right_cell':
      ManageIQ.explorer.processReplaceRightCell(data);
      break;
    case 'replace_main_div':
      ManageIQ.explorer.processReplaceMainDiv(data);
      break;
    case 'buttons':
      ManageIQ.explorer.processButtons(data);
      break;
    case 'window':
      ManageIQ.explorer.processWindow(data);
      break;
  }
};

ManageIQ.explorer.processWindow = function(data) {
  if (_.isString(data.openUrl)) {
    window.open(data.openUrl);
  }
  ManageIQ.explorer.spinnerOff(data);
};

ManageIQ.explorer.processButtons = function(data) {
  ManageIQ.explorer.miqButtons(data);
};

ManageIQ.explorer.processReplaceMainDiv = function(data) {
  ManageIQ.explorer.updatePartials(data);
};

ManageIQ.explorer.processFlash = function(data) {
  ManageIQ.explorer.replacePartials(data);
  ManageIQ.explorer.spinnerOff(data);
  ManageIQ.explorer.scrollTop(data);
  ManageIQ.explorer.focus(data);

  if (!_.isUndefined(data.activateNode)) {
    miqTreeActivateNode(data.activateNode.tree, data.activateNode.node);
  }
};

ManageIQ.explorer.replacePartials = function(data) {
  if (_.isObject(data.replacePartials)) {
    _.forEach(data.replacePartials, function (content, element) {
      $('#' + element).replaceWith(content);
    });
  }
};

ManageIQ.explorer.updatePartials = function(data) {
  if (_.isObject(data.updatePartials)) {
    _.forEach(data.updatePartials, function (content, element) {
      $('#' + element).html(content);
    });
  }
};

ManageIQ.explorer.spinnerOff = function(data) {
  if (data.spinnerOff) {
    miqSparkle(false);
  }
};

ManageIQ.explorer.scrollTop = function(data) {
  if (data.scrollTop) {
    $('#main_div').scrollTop(0);
  }
};

ManageIQ.explorer.miqButtons = function(data) {
  miqButtons(data.showMiqButtons ? 'show' : 'hide');
};

ManageIQ.explorer.focus = function(data) {
  if (_.isString(data.focus)) {
    var element = $('#' + data.focus);
    if ( element.length ) element.focus();
  }
}

ManageIQ.explorer.processReplaceRightCell = function(data) {
  /* variables for the expression editor */
  if (_.isObject(data.expEditor)) {
    if (_.isObject(data.expEditor.first)) {
      if (!_.isUndefined(data.expEditor.first.type)) {
        ManageIQ.expEditor.first.type   = data.expEditor.first.type;
      }
      if (!_.isUndefined(data.expEditor.first.title)) {
        ManageIQ.expEditor.first.title  = data.expEditor.first.title;
      }
    }

    if (_.isObject(data.expEditor.second)) {
      if (!_.isUndefined(data.expEditor.second.type)) {
        ManageIQ.expEditor.second.type   = data.expEditor.second.type;
      }
      if (!_.isUndefined(data.expEditor.second.title)) {
        ManageIQ.expEditor.second.title  = data.expEditor.second.title;
      }
    }
  }

  ManageIQ.explorer.miqButtons(data);

  if (_.isString(data.clearTreeCookies)) { miqDeleteTreeCookies(data.clearTreeCookies); }

  if (_.isString(data.accordionSwap)) {
    miqAccordionSwap('#accordion .panel-collapse.collapse.in', '#' + data.accordionSwap + '_accord');
  }

  /* dealing with tree nodes */
  if (!_.isUndefined(data.addNodes)) {

    if (data.addNodes.remove) {
      miqRemoveNodeChildren(data.addNodes.activeTree, data.addNodes.key);
    }

    miqAddNodeChildren(data.addNodes.activeTree,
                       data.addNodes.key,
                       data.addNodes.osf,
                       data.addNodes.nodes);
  }

  if (!_.isUndefined(data.deleteNode)) {
    var del_node = miqTreeFindNodeByKey(data.deleteNode.activeTree, data.deleteNode.node)
    miqTreeObject(data.deleteNode.activeTree).deleteNode(del_node);
  }

  if (_.isString(data.dashboardUrl)) {
    ManageIQ.widget.dashboardUrl = data.dashboardUrl;
  }

  if ($('#advsearchModal').hasClass('modal fade in')) {
    $('#advsearchModal').modal('hide');
  }

  ManageIQ.explorer.updatePartials(data);

  if (_.isObject(data.updateElements)) {
    _.forEach(data.updateElements, function (options, element) {
        ManageIQ.explorer.updateElement(element, options);
    });
  }

  ManageIQ.explorer.replacePartials(data);

  if (_.isObject(data.buildCalendar)) { ManageIQ.explorer.buildCalendar(data.buildCalendar); }

  if (data.initDashboard) { miqInitDashboardCols(); }

  if (data.clearGtlListGrid) { ManageIQ.grids.gtl_list_grid = undefined; }

  if (_.isObject(data.setVisibility))
    _.forEach(data.setVisibility, function (visible, element) {
      if ( miqDomElementExists(element) ) {
        if ( visible ) {
          $('#' + element).show()
        } else {
          $('#' + element).hide()
        }
      }
    });

  ManageIQ.explorer.scrollTop(data);

  if (_.isString(data.rightCellText)) {
    $('h1#explorer_title > span#explorer_title_text')
      .html(data.rightCellText);
  }

  if (_.isArray(data.reloadToolbars) && data.reloadToolbars.length) {
    ManageIQ.angular.rxSubject.onNext({
      redrawToolbar: data.reloadToolbars
    });
  } else if (_.isObject(data.reloadToolbars) && !_.isArray(data.reloadToolbars)) {
    // FIXME remove this branch completely once sure
    console.error('Found a toolbar using the obsolete path! Please report or fix');

    _.forEach(data.reloadToolbars, function (content, element) {
      $('#' + element).html(content);
    });
    miqInitToolbars();
  }

  ManageIQ.record = data.record;

  if (!_.isUndefined(data.activateNode)) {
    miqExpandParentNodes(data.activateNode.activeTree, data.activateNode.osf);
    miqTreeActivateNodeSilently(data.activateNode.activeTree, data.activateNode.osf);
  }

  if (_.isObject(data.lockTrees)) {
    _.forEach(data.lockTrees, function (lock, tree) {
      ManageIQ.explorer.lock_tree(tree, lock);
    });
  }

  if (_.isObject(data.chartData)) {
    ManageIQ.charts.chartData = data.chartData;
    // FIXME:  @out << Charting.js_load_statement(true)
  }

  if (data.resetChanges) { ManageIQ.changes = null; }
  if (data.resetOneTrans) { ManageIQ.oneTransition.oneTrans = 0; }
  if (data.oneTransIE) { ManageIQ.oneTransition.IEButtonPressed = true; }

  ManageIQ.explorer.focus(data);

  if (!_.isUndefined(data.clearSearch)) {
    ManageIQ.explorer.clearSearchToggle(data.clearSearch);
  }

  miqInitMainContent();
  miqInitAccordions();

  if (data.hideModal) { $('#quicksearchbox').modal('hide'); }
  if (data.initAccords) { miqInitAccordions(); }

  if (_.isString(data.ajaxUrl)) {
    miqAsyncAjax(data.ajaxUrl);
  } else {
    miqSparkleOff();
  }

  return null;
};
<|endoftext|>


<commit_before>
  /***
   * @package Core
   * @description Internal utility and common methods.
   ***/

  // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
  var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, math = Math, Undefined;

  // Internal toString
  var internalToString = object.prototype.toString;

  // The global context
  var globalContext = typeof global !== 'undefined' ? global : this;

  // Type check methods need a way to be accessed dynamically outside global context.
  var typeChecks = {};

  // defineProperty exists in IE8 but will error when trying to define a property on
  // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
  var definePropertySupport = object.defineProperty && object.defineProperties;

  // Class initializers and class helpers

  var ClassNames = 'Array,Boolean,Date,Function,Number,String,RegExp'.split(',');

  var isArray    = buildClassCheck(ClassNames[0]);
  var isBoolean  = buildClassCheck(ClassNames[1]);
  var isDate     = buildClassCheck(ClassNames[2]);
  var isFunction = buildClassCheck(ClassNames[3]);
  var isNumber   = buildClassCheck(ClassNames[4]);
  var isString   = buildClassCheck(ClassNames[5]);
  var isRegExp   = buildClassCheck(ClassNames[6]);

  function buildClassCheck(name) {
    var type, fn;
    if(/String|Number|Boolean/.test(name)) {
      type = name.toLowerCase();
    }
    fn = (name === 'Array' && array.isArray) || function(obj, klass) {
      if(type && typeof obj === type) {
        return true;
      }
      klass = klass || className(obj);
      return klass === '[object '+name+']';
    }
    typeChecks[name] = fn;
    return fn;
  }

  function className(obj) {
    return internalToString.call(obj);
  }

  function initializeClasses() {
    initializeClass(object);
    iterateOverObject(ClassNames, function(i,name) {
      initializeClass(globalContext[name]);
    });
  }

  function initializeClass(klass) {
    if(klass['SugarMethods']) return;
    defineProperty(klass, 'SugarMethods', {});
    extend(klass, false, false, {
      'extend': function(methods, override, instance) {
        extend(klass, instance !== false, override, methods);
      },
      'sugarRestore': function() {
        return batchMethodExecute(klass, arguments, function(target, name, m) {
          defineProperty(target, name, m.method);
        });
      },
      'sugarRevert': function() {
        return batchMethodExecute(klass, arguments, function(target, name, m) {
          if(m.existed) {
            defineProperty(target, name, m.original);
          } else {
            delete target[name];
          }
        });
      }
    });
  }

  // Class extending methods

  function extend(klass, instance, override, methods) {
    var extendee = instance ? klass.prototype : klass;
    initializeClass(klass);
    iterateOverObject(methods, function(name, method) {
      var original = extendee[name];
      var existed  = hasOwnProperty(extendee, name);
      if(typeof override === 'function') {
        method = wrapNative(extendee[name], method, override);
      }
      if(override !== false || !extendee[name]) {
        defineProperty(extendee, name, method);
      }
      // If the method is internal to Sugar, then store a reference so it can be restored later.
      klass['SugarMethods'][name] = { instance: instance, method: method, original: original, existed: existed };
    });
  }

  function extendSimilar(klass, instance, override, set, fn) {
    var methods = {};
    set = isString(set) ? set.split(',') : set;
    set.forEach(function(name, i) {
      fn(methods, name, i);
    });
    extend(klass, instance, override, methods);
  }

  function batchMethodExecute(klass, args, fn) {
    var all = args.length === 0, methods = multiArgs(args), changed = false;
    iterateOverObject(klass['SugarMethods'], function(name, m) {
      if(all || methods.indexOf(name) !== -1) {
        changed = true;
        fn(m.instance ? klass.prototype : klass, name, m);
      }
    });
    return changed;
  }

  function wrapNative(nativeFn, extendedFn, condition) {
    return function() {
      var fn;
      if(nativeFn && (condition === true || !condition.apply(this, arguments))) {
        fn = nativeFn;
      } else {
        fn = extendedFn;
      }
      return fn.apply(this, arguments);
    }
  }

  function defineProperty(target, name, method) {
    if(definePropertySupport) {
      object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
    } else {
      target[name] = method;
    }
  }

  // Argument helpers

  function multiArgs(args, fn) {
    var result = [], i, len;
    for(i = 0, len = args.length; i < len; i++) {
      result.push(args[i]);
      if(fn) fn.call(args, args[i], i);
    }
    return result;
  }

  function flattenedArgs(obj, fn, from) {
    multiArgs(array.prototype.concat.apply([], array.prototype.slice.call(obj, from || 0)), fn);
  }

  function checkCallback(fn) {
    if(!fn || !fn.call) {
      throw new TypeError('Callback is not callable');
    }
  }

  // General helpers

  function isDefined(o) {
    return o !== Undefined;
  }

  function isUndefined(o) {
    return o === Undefined;
  }

  // Object helpers

  function isObjectPrimitive(obj) {
    // Check for null
    return obj && typeof obj === 'object';
  }

  function isPlainObject(obj, klass) {
    klass = klass || className(obj);
    try {
      // Not own constructor property must be Object
      // This code was borrowed from jQuery.isPlainObject
      if (obj.constructor &&
            !hasOwnProperty(obj, 'constructor') &&
            !hasOwnProperty(obj.constructor.prototype, 'isPrototypeOf')) {
        return false;
      }
    } catch (e) {
      // IE8,9 Will throw exceptions on certain host objects.
      return false;
    }
    // === on the constructor is not safe across iframes
    // 'hasOwnProperty' ensures that the object also inherits
    // from Object, which is false for DOMElements in IE.
    // AP: CAN THIS NOW BE REMOVED WITH THE CODE ABOVE IN PLACE???
    return !!obj && klass === '[object Object]' && 'hasOwnProperty' in obj;
  }

  function hasOwnProperty(obj, key) {
    return object['hasOwnProperty'].call(obj, key);
  }

  function iterateOverObject(obj, fn) {
    var key;
    for(key in obj) {
      if(!hasOwnProperty(obj, key)) continue;
      if(fn.call(obj, key, obj[key], obj) === false) break;
    }
  }

  function simpleRepeat(n, fn) {
    for(var i = 0; i < n; i++) {
      fn(i);
    }
  }

  function simpleMerge(target, source) {
    iterateOverObject(source, function(key) {
      target[key] = source[key];
    });
    return target;
  }

  // Hash definition

  function Hash(obj) {
    simpleMerge(this, obj);
  };

  Hash.prototype.constructor = object;

  // Number helpers

  function round(val, precision, method) {
    var fn = math[method || 'round'];
    var multiplier = math.pow(10, math.abs(precision || 0));
    if(precision < 0) multiplier = 1 / multiplier;
    return fn(val * multiplier) / multiplier;
  }

  function ceil(val, precision) {
    return round(val, precision, 'ceil');
  }

  function floor(val, precision) {
    return round(val, precision, 'floor');
  }

  // Used by Number and Date

  function padNumber(num, place, sign, base) {
    var str = math.abs(num).toString(base || 10);
    str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str;
    if(sign || num < 0) {
      str = (num < 0 ? '-' : '+') + str;
    }
    return str;
  }

  function getOrdinalizedSuffix(num) {
    if(num >= 11 && num <= 13) {
      return 'th';
    } else {
      switch(num % 10) {
        case 1:  return 'st';
        case 2:  return 'nd';
        case 3:  return 'rd';
        default: return 'th';
      }
    }
  }

  // String helpers

  // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
  function getTrimmableCharacters() {
    return '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF';
  }

  function repeatString(times, str) {
    return array(math.max(0, isDefined(times) ? times : 1) + 1).join(str || '');
  }

  // RegExp helpers

  function getRegExpFlags(reg, add) {
    var flags = reg.toString().match(/[^/]*$/)[0];
    if(add) {
      flags = (flags + add).split('').sort().join('').replace(/([gimy])\1+/g, '$1');
    }
    return flags;
  }

  function escapeRegExp(str) {
    if(!isString(str)) str = string(str);
    return str.replace(/([\\/'*+?|()\[\]{}.^$])/g,'\\$1');
  }

  // Date helpers

  function callDateGet(d, method) {
    return d['get' + (d._utc ? 'UTC' : '') + method]();
  }

  function callDateSet(d, method, value) {
    return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
  }

  function extractDurationFromString(str) {
    var match, val, unit;
    match = str.toLowerCase().match(/^(\d+)?\s?(\w+?)s?$/i);
    val = parseInt(match[1]) || 1;
    unit = match[2].slice(0,1).toUpperCase() + match[2].slice(1);
    if(unit.match(/hour|minute|second/i)) {
      unit += 's';
    } else if(unit === 'Year') {
      unit = 'FullYear';
    } else if(unit === 'Day') {
      unit = 'Date';
    }
    return [val, unit];
  }

  // Specialized helpers

  // Used by Array#unique and Object.equal

  function stringify(thing, stack) {
    var type = typeof thing,
        thingIsObject,
        thingIsArray,
        klass, value,
        arr, key, i, len;

    // Return quickly if string to save cycles
    if(type === 'string') return thing;

    klass         = internalToString.call(thing)
    thingIsArray  = klass === '[object Array]';
    thingIsObject = isObjectPrimitive(thing) && !thingIsArray;

    if(thing != null && thingIsObject || thingIsArray) {
      // This method for checking for cyclic structures was egregiously stolen from
      // the ingenious method by @kitcambridge from the Underscore script:
      // https://github.com/documentcloud/underscore/issues/240
      if(!stack) stack = [];
      // Allowing a step into the structure before triggering this
      // script to save cycles on standard JSON structures and also to
      // try as hard as possible to catch basic properties that may have
      // been modified.
      if(stack.length > 1) {
        i = stack.length;
        while (i--) {
          if (stack[i] === thing) {
            return 'CYC';
          }
        }
      }
      stack.push(thing);
      value = thing.valueOf() + string(thing.constructor);
      arr = thingIsArray ? thing : object.keys(thing).sort();
      for(i = 0, len = arr.length; i < len; i++) {
        key = thingIsArray ? i : arr[i];
        value += key + stringify(thing[key], stack);
      }
      stack.pop();
    } else if(1 / thing === -Infinity) {
      value = '-0';
    } else {
      value = string(thing && thing.valueOf ? thing.valueOf() : thing);
    }
    return type + klass + value;
  }

  function isEqual(a, b) {
    if(objectIsMatchedByValue(a) && objectIsMatchedByValue(b)) {
      return stringify(a) === stringify(b);
    } else {
      return a === b;
    }
  }

  function objectIsMatchedByValue(obj) {
    // Only known objects are matched by value. This is notably excluding functions, DOM Elements, and instances of
    // user-created classes. The latter can arguably be matched by value, but distinguishing between these and
    // host objects -- which should never be compared by value -- is very tricky so not dealing with it here.
    var klass = className(obj);
    return /^\[object Date|Array|String|Number|RegExp|Boolean|Arguments\]$/.test(klass) || isPlainObject(obj, klass);
  }

  // Used by Array#at and String#at

  function entryAtIndex(arr, args, str) {
    var result = [], length = arr.length, loop = args[args.length - 1] !== false, r;
    multiArgs(args, function(index) {
      if(isBoolean(index)) return false;
      if(loop) {
        index = index % length;
        if(index < 0) index = length + index;
      }
      r = str ? arr.charAt(index) || '' : arr[index];
      result.push(r);
    });
    return result.length < 2 ? result[0] : result;
  }

  // Object class methods implemented as instance methods

  function buildObjectInstanceMethods(set, target) {
    extendSimilar(target, true, false, set, function(methods, name) {
      methods[name + (name === 'equal' ? 's' : '')] = function() {
        return object[name].apply(null, [this].concat(multiArgs(arguments)));
      }
    });
  }

  initializeClasses();

<commit_msg>stringify needs to check for a plain object<commit_after>
  /***
   * @package Core
   * @description Internal utility and common methods.
   ***/

  // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
  var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, math = Math, Undefined;

  // Internal toString
  var internalToString = object.prototype.toString;

  // The global context
  var globalContext = typeof global !== 'undefined' ? global : this;

  // Type check methods need a way to be accessed dynamically outside global context.
  var typeChecks = {};

  // defineProperty exists in IE8 but will error when trying to define a property on
  // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
  var definePropertySupport = object.defineProperty && object.defineProperties;

  // Class initializers and class helpers

  var ClassNames = 'Array,Boolean,Date,Function,Number,String,RegExp'.split(',');

  var isArray    = buildClassCheck(ClassNames[0]);
  var isBoolean  = buildClassCheck(ClassNames[1]);
  var isDate     = buildClassCheck(ClassNames[2]);
  var isFunction = buildClassCheck(ClassNames[3]);
  var isNumber   = buildClassCheck(ClassNames[4]);
  var isString   = buildClassCheck(ClassNames[5]);
  var isRegExp   = buildClassCheck(ClassNames[6]);

  function buildClassCheck(name) {
    var type, fn;
    if(/String|Number|Boolean/.test(name)) {
      type = name.toLowerCase();
    }
    fn = (name === 'Array' && array.isArray) || function(obj, klass) {
      if(type && typeof obj === type) {
        return true;
      }
      klass = klass || className(obj);
      return klass === '[object '+name+']';
    }
    typeChecks[name] = fn;
    return fn;
  }

  function className(obj) {
    return internalToString.call(obj);
  }

  function initializeClasses() {
    initializeClass(object);
    iterateOverObject(ClassNames, function(i,name) {
      initializeClass(globalContext[name]);
    });
  }

  function initializeClass(klass) {
    if(klass['SugarMethods']) return;
    defineProperty(klass, 'SugarMethods', {});
    extend(klass, false, false, {
      'extend': function(methods, override, instance) {
        extend(klass, instance !== false, override, methods);
      },
      'sugarRestore': function() {
        return batchMethodExecute(klass, arguments, function(target, name, m) {
          defineProperty(target, name, m.method);
        });
      },
      'sugarRevert': function() {
        return batchMethodExecute(klass, arguments, function(target, name, m) {
          if(m.existed) {
            defineProperty(target, name, m.original);
          } else {
            delete target[name];
          }
        });
      }
    });
  }

  // Class extending methods

  function extend(klass, instance, override, methods) {
    var extendee = instance ? klass.prototype : klass;
    initializeClass(klass);
    iterateOverObject(methods, function(name, method) {
      var original = extendee[name];
      var existed  = hasOwnProperty(extendee, name);
      if(typeof override === 'function') {
        method = wrapNative(extendee[name], method, override);
      }
      if(override !== false || !extendee[name]) {
        defineProperty(extendee, name, method);
      }
      // If the method is internal to Sugar, then store a reference so it can be restored later.
      klass['SugarMethods'][name] = { instance: instance, method: method, original: original, existed: existed };
    });
  }

  function extendSimilar(klass, instance, override, set, fn) {
    var methods = {};
    set = isString(set) ? set.split(',') : set;
    set.forEach(function(name, i) {
      fn(methods, name, i);
    });
    extend(klass, instance, override, methods);
  }

  function batchMethodExecute(klass, args, fn) {
    var all = args.length === 0, methods = multiArgs(args), changed = false;
    iterateOverObject(klass['SugarMethods'], function(name, m) {
      if(all || methods.indexOf(name) !== -1) {
        changed = true;
        fn(m.instance ? klass.prototype : klass, name, m);
      }
    });
    return changed;
  }

  function wrapNative(nativeFn, extendedFn, condition) {
    return function() {
      var fn;
      if(nativeFn && (condition === true || !condition.apply(this, arguments))) {
        fn = nativeFn;
      } else {
        fn = extendedFn;
      }
      return fn.apply(this, arguments);
    }
  }

  function defineProperty(target, name, method) {
    if(definePropertySupport) {
      object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
    } else {
      target[name] = method;
    }
  }

  // Argument helpers

  function multiArgs(args, fn) {
    var result = [], i, len;
    for(i = 0, len = args.length; i < len; i++) {
      result.push(args[i]);
      if(fn) fn.call(args, args[i], i);
    }
    return result;
  }

  function flattenedArgs(obj, fn, from) {
    multiArgs(array.prototype.concat.apply([], array.prototype.slice.call(obj, from || 0)), fn);
  }

  function checkCallback(fn) {
    if(!fn || !fn.call) {
      throw new TypeError('Callback is not callable');
    }
  }

  // General helpers

  function isDefined(o) {
    return o !== Undefined;
  }

  function isUndefined(o) {
    return o === Undefined;
  }

  // Object helpers

  function isObjectPrimitive(obj) {
    // Check for null
    return obj && typeof obj === 'object';
  }

  function isPlainObject(obj, klass) {
    klass = klass || className(obj);
    try {
      // Not own constructor property must be Object
      // This code was borrowed from jQuery.isPlainObject
      if (obj.constructor &&
            !hasOwnProperty(obj, 'constructor') &&
            !hasOwnProperty(obj.constructor.prototype, 'isPrototypeOf')) {
        return false;
      }
    } catch (e) {
      // IE8,9 Will throw exceptions on certain host objects.
      return false;
    }
    // === on the constructor is not safe across iframes
    // 'hasOwnProperty' ensures that the object also inherits
    // from Object, which is false for DOMElements in IE.
    // AP: CAN THIS NOW BE REMOVED WITH THE CODE ABOVE IN PLACE???
    return !!obj && klass === '[object Object]' && 'hasOwnProperty' in obj;
  }

  function hasOwnProperty(obj, key) {
    return object['hasOwnProperty'].call(obj, key);
  }

  function iterateOverObject(obj, fn) {
    var key;
    for(key in obj) {
      if(!hasOwnProperty(obj, key)) continue;
      if(fn.call(obj, key, obj[key], obj) === false) break;
    }
  }

  function simpleRepeat(n, fn) {
    for(var i = 0; i < n; i++) {
      fn(i);
    }
  }

  function simpleMerge(target, source) {
    iterateOverObject(source, function(key) {
      target[key] = source[key];
    });
    return target;
  }

  // Hash definition

  function Hash(obj) {
    simpleMerge(this, obj);
  };

  Hash.prototype.constructor = object;

  // Number helpers

  function round(val, precision, method) {
    var fn = math[method || 'round'];
    var multiplier = math.pow(10, math.abs(precision || 0));
    if(precision < 0) multiplier = 1 / multiplier;
    return fn(val * multiplier) / multiplier;
  }

  function ceil(val, precision) {
    return round(val, precision, 'ceil');
  }

  function floor(val, precision) {
    return round(val, precision, 'floor');
  }

  // Used by Number and Date

  function padNumber(num, place, sign, base) {
    var str = math.abs(num).toString(base || 10);
    str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str;
    if(sign || num < 0) {
      str = (num < 0 ? '-' : '+') + str;
    }
    return str;
  }

  function getOrdinalizedSuffix(num) {
    if(num >= 11 && num <= 13) {
      return 'th';
    } else {
      switch(num % 10) {
        case 1:  return 'st';
        case 2:  return 'nd';
        case 3:  return 'rd';
        default: return 'th';
      }
    }
  }

  // String helpers

  // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
  function getTrimmableCharacters() {
    return '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF';
  }

  function repeatString(times, str) {
    return array(math.max(0, isDefined(times) ? times : 1) + 1).join(str || '');
  }

  // RegExp helpers

  function getRegExpFlags(reg, add) {
    var flags = reg.toString().match(/[^/]*$/)[0];
    if(add) {
      flags = (flags + add).split('').sort().join('').replace(/([gimy])\1+/g, '$1');
    }
    return flags;
  }

  function escapeRegExp(str) {
    if(!isString(str)) str = string(str);
    return str.replace(/([\\/'*+?|()\[\]{}.^$])/g,'\\$1');
  }

  // Date helpers

  function callDateGet(d, method) {
    return d['get' + (d._utc ? 'UTC' : '') + method]();
  }

  function callDateSet(d, method, value) {
    return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
  }

  function extractDurationFromString(str) {
    var match, val, unit;
    match = str.toLowerCase().match(/^(\d+)?\s?(\w+?)s?$/i);
    val = parseInt(match[1]) || 1;
    unit = match[2].slice(0,1).toUpperCase() + match[2].slice(1);
    if(unit.match(/hour|minute|second/i)) {
      unit += 's';
    } else if(unit === 'Year') {
      unit = 'FullYear';
    } else if(unit === 'Day') {
      unit = 'Date';
    }
    return [val, unit];
  }

  // Specialized helpers

  // Used by Array#unique and Object.equal

  function stringify(thing, stack) {
    var type = typeof thing,
        thingIsObject,
        thingIsArray,
        klass, value,
        arr, key, i, len;

    // Return quickly if string to save cycles
    if(type === 'string') return thing;

    klass         = internalToString.call(thing)
    thingIsObject = isPlainObject(thing, klass);
    thingIsArray  = isArray(thing, klass);

    if(thing != null && thingIsObject || thingIsArray) {
      // This method for checking for cyclic structures was egregiously stolen from
      // the ingenious method by @kitcambridge from the Underscore script:
      // https://github.com/documentcloud/underscore/issues/240
      if(!stack) stack = [];
      // Allowing a step into the structure before triggering this
      // script to save cycles on standard JSON structures and also to
      // try as hard as possible to catch basic properties that may have
      // been modified.
      if(stack.length > 1) {
        i = stack.length;
        while (i--) {
          if (stack[i] === thing) {
            return 'CYC';
          }
        }
      }
      stack.push(thing);
      value = thing.valueOf() + string(thing.constructor);
      arr = thingIsArray ? thing : object.keys(thing).sort();
      for(i = 0, len = arr.length; i < len; i++) {
        key = thingIsArray ? i : arr[i];
        value += key + stringify(thing[key], stack);
      }
      stack.pop();
    } else if(1 / thing === -Infinity) {
      value = '-0';
    } else {
      value = string(thing && thing.valueOf ? thing.valueOf() : thing);
    }
    return type + klass + value;
  }

  function isEqual(a, b) {
    if(objectIsMatchedByValue(a) && objectIsMatchedByValue(b)) {
      return stringify(a) === stringify(b);
    } else {
      return a === b;
    }
  }

  function objectIsMatchedByValue(obj) {
    // Only known objects are matched by value. This is notably excluding functions, DOM Elements, and instances of
    // user-created classes. The latter can arguably be matched by value, but distinguishing between these and
    // host objects -- which should never be compared by value -- is very tricky so not dealing with it here.
    var klass = className(obj);
    return /^\[object Date|Array|String|Number|RegExp|Boolean|Arguments\]$/.test(klass) || isPlainObject(obj, klass);
  }

  // Used by Array#at and String#at

  function entryAtIndex(arr, args, str) {
    var result = [], length = arr.length, loop = args[args.length - 1] !== false, r;
    multiArgs(args, function(index) {
      if(isBoolean(index)) return false;
      if(loop) {
        index = index % length;
        if(index < 0) index = length + index;
      }
      r = str ? arr.charAt(index) || '' : arr[index];
      result.push(r);
    });
    return result.length < 2 ? result[0] : result;
  }

  // Object class methods implemented as instance methods

  function buildObjectInstanceMethods(set, target) {
    extendSimilar(target, true, false, set, function(methods, name) {
      methods[name + (name === 'equal' ? 's' : '')] = function() {
        return object[name].apply(null, [this].concat(multiArgs(arguments)));
      }
    });
  }

  initializeClasses();

<|endoftext|>


<commit_before>var transforms = require('./transforms');
var split = require('split');
var yargs = require('yargs');

// TODO [--data-type=<data-type>[,<data-type>]]
// TODO support tree qualifier for filenames
module.exports.run = function() {
  var args, subcommand, data, extra;

  args = yargs
    .usage("Usage: $0 <subcommand> <data-type> [<param>[ <param>]]")
    .example("$0 convert array", "convert input to a json array")
    .example("$0 convert template <template-path>", "convert input to a templated output")
    .example("$0 acc template <template-path>", "gather all input into templated output")
    .example("$0 files list", "list the files at the root of the project")
    .example("$0 files json", "output json for a file")
    .example("$0 files out", "output content of a file")
    .example("$0 blob out", "output blob for a sha")
    .example("$0 issues open", "list the open issues, less PRs")
    .example("$0 issues oldest", "list the oldest issue date")
    .example("$0 repos list", "list all repos for a user")
    .example("$0 search list", "search npm for a package")
    .example("$0 tags list", "list all tags for a repo")
    .example("$0 tags newest", "show the highest semver tag for a repo")
    .demand(2)
    .check(function(args) {
      var supported;

      subcommand = args['_'][0];
      data = args['_'][1];
      extra = args['_'].slice(2);

      if( !transforms[subcommand] ){
        throw new Error("unsupported subcommand: " + subcommand);
      }

      supported = transforms[subcommand].supported[data];

      if( !supported ){
        throw new Error("unsupported data type: " + data);
      }

      if( typeof supported === "number" && supported > extra.length ){
        throw new Error("data type requires more arguments");
      }
    })
    .argv;

  var stream = process.stdin.pipe(split());

  if( subcommand == "files" && data == "out" ) {
    stream = stream
      .pipe(new transforms.files("json", extra))
      .pipe(new transforms.reposha(data))
      .pipe(new transforms.blob(data, extra));
  } else {
    stream = stream.pipe(new transforms[subcommand]( data, extra ));
  }

  stream.pipe(process.stdout);
};
<commit_msg>error handling and exit status<commit_after>var transforms = require('./transforms');
var split = require('split');
var yargs = require('yargs');

// TODO [--data-type=<data-type>[,<data-type>]]
// TODO support tree qualifier for filenames
module.exports.run = function() {
  var args, subcommand, data, extra;

  args = yargs
    .usage("Usage: $0 <subcommand> <data-type> [<param>[ <param>]]")
    .example("$0 convert array", "convert input to a json array")
    .example("$0 convert template <template-path>", "convert input to a templated output")
    .example("$0 acc template <template-path>", "gather all input into templated output")
    .example("$0 files list", "list the files at the root of the project")
    .example("$0 files json", "output json for a file")
    .example("$0 files out", "output content of a file")
    .example("$0 blob out", "output blob for a sha")
    .example("$0 issues open", "list the open issues, less PRs")
    .example("$0 issues oldest", "list the oldest issue date")
    .example("$0 repos list", "list all repos for a user")
    .example("$0 search list", "search npm for a package")
    .example("$0 tags list", "list all tags for a repo")
    .example("$0 tags newest", "show the highest semver tag for a repo")
    .demand(2)
    .check(function(args) {
      var supported;

      subcommand = args['_'][0];
      data = args['_'][1];
      extra = args['_'].slice(2);

      if( !transforms[subcommand] ){
        throw new Error("unsupported subcommand: " + subcommand);
      }

      supported = transforms[subcommand].supported[data];

      if( !supported ){
        throw new Error("unsupported data type: " + data);
      }

      if( typeof supported === "number" && supported > extra.length ){
        throw new Error("data type requires more arguments");
      }
    })
    .argv;

  var exit, stream;

  stream = process.stdin.pipe(split());

  exit = function( err ) {
    console.log(err.stack);
    process.exit(1);
  };

  if( subcommand == "files" && data == "out" ) {
    stream = stream
      .pipe(new transforms.files("json", extra))
      .on( 'error', exit )
      .pipe(new transforms.reposha(data))
      .on( 'error', exit )
      .pipe(new transforms.blob(data, extra))
      .on( 'error', exit );
  } else {
    stream = stream.pipe(new transforms[subcommand]( data, extra ));
  }

  stream = stream.pipe(process.stdout);
};
<|endoftext|>