<commit_before>import classNames from "classnames"
import { CompactRow } from "components/Compact"
import LinkTo from "components/LinkTo"
import RemoveButton from "components/RemoveButton"
import _cloneDeep from "lodash/cloneDeep"
import _get from "lodash/get"
import _isEmpty from "lodash/isEmpty"
import PropTypes from "prop-types"
import React, { useCallback, useMemo } from "react"
import {
  Button,
  Col,
  Form,
  FormControl,
  FormGroup,
  InputGroup,
  Row,
  ToggleButton,
  ToggleButtonGroup
} from "react-bootstrap"
import utils from "utils"
const getFieldId = field => field.id || field.name // name property is required

const getHumanValue = (field, humanValue) => {
  if (typeof humanValue === "function") {
    return humanValue(field.value)
  } else if (humanValue !== undefined) {
    return humanValue
  } else {
    return field.value
  }
}

const getFormGroupValidationState = (field, form) => {
  const { touched, errors } = form
  const fieldTouched = _get(touched, field.name)
  const fieldError = _get(errors, field.name)
  return { validationState: fieldTouched && fieldError, fieldError }
}

const getHelpBlock = (field, form) => {
  const { validationState, fieldError } = getFormGroupValidationState(
    field,
    form
  )
  return (
    validationState && (
      <FormControl.Feedback type="invalid">{fieldError}</FormControl.Feedback>
    )
  )
}

const FieldNoLabel = ({ field, form, widgetElem, children }) => {
  const id = getFieldId(field)
  return (
    <FormGroup id={`fg-${id}`} controlId={id}>
      {widgetElem}
      {getHelpBlock(field, form)}
      {children}
    </FormGroup>
  )
}
FieldNoLabel.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  widgetElem: PropTypes.object,
  children: PropTypes.any
}

const Field = ({
  field,
  form,
  label,
  widgetElem,
  children,
  extraColElem,
  addon,
  vertical,
  isCompact,
  extraAddon,
  labelColumnWidth,
  className
}) => {
  const id = getFieldId(field)
  const widget = useMemo(
    () =>
      !addon ? (
        widgetElem
      ) : (
        <InputGroup>
          {widgetElem}
          {extraAddon && <InputGroup.Addon>{extraAddon}</InputGroup.Addon>}
          <FieldAddon id={id} addon={addon} />
        </InputGroup>
      ),
    [addon, extraAddon, id, widgetElem]
  )

  if (label === undefined) {
    label = utils.sentenceCase(field.name) // name is a required prop of field
  }
  // setting label or extraColElem explicitly to null will completely remove these columns!
  const widgetWidth =
    12 -
    (label === null ? 0 : labelColumnWidth) -
    (extraColElem === null ? 0 : 3)
  // controlId prop of the FormGroup sets the id of the control element

  if (isCompact) {
    return (
      <CompactRow
        label={label}
        className={className}
        content={
          <>
            {widget}
            {getHelpBlock(field, form)}
            {extraColElem}
            {children}
          </>
        }
      />
    )
  }

  return (
    <FormGroup id={`fg-${id}`} controlId={id} className={className}>
      {vertical ? (
        <>

{label !== null && <Form.Label>{label}</Form.Label>}

          {widget}
          {getHelpBlock(field, form)}
          {children}
        </>
      ) : (
        <Row style={{ marginBottom: "1rem" }}>
          {label !== null && (
            <Col sm={labelColumnWidth} as={Form.Label}>
              {label}
            </Col>
          )}
          <Col sm={widgetWidth}>

              {widget}
              {getHelpBlock(field, form)}
              {children}

          </Col>
          {extraColElem && <Col sm={3} {...extraColElem.props} />}
        </Row>
      )}
    </FormGroup>
  )
}
Field.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  widgetElem: PropTypes.object,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  extraAddon: PropTypes.object,
  isCompact: PropTypes.bool,
  labelColumnWidth: PropTypes.number,
  className: PropTypes.string
}
Field.defaultProps = {
  vertical: false, // default direction of label and input = horizontal
  labelColumnWidth: 2
}

export const InputField = ({
  field, // { name, value, onChange, onBlur }
  form, // contains, touched, errors, values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  asA,
  inputType,
  children,
  extraColElem,
  addon,
  vertical,
  extraAddon,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  const widgetElem = useMemo(
    () => (
      <FormControl
        type={inputType}
        {...Object.without(field, "value")}
        value={utils.isNullOrUndefined(field.value) ? "" : field.value}
        {...otherProps}
        as={asA ?? "input"}
        isInvalid={validationState}
      />
    ),
    [field, otherProps, inputType, asA, validationState]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
      extraAddon={extraAddon}
    />
  )
}
InputField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  asA: PropTypes.string,
  inputType: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  extraAddon: PropTypes.object
}

export const InputFieldNoLabel = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  children,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  const widgetElem = useMemo(
    () => (
      <FormControl
        {...Object.without(field, "value")}
        value={utils.isNullOrUndefined(field.value) ? "" : field.value}
        {...otherProps}
        isInvalid={validationState}
      />
    ),
    [field, otherProps, validationState]
  )
  return (
    <FieldNoLabel
      field={field}
      form={form}
      widgetElem={widgetElem}
      children={children}
    />
  )
}
InputFieldNoLabel.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  children: PropTypes.any
}

export const ReadonlyField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  children,
  extraColElem,
  labelColumnWidth,
  addon,
  vertical,
  humanValue,
  isCompact,
  ...otherProps
}) => {
  const { className } = otherProps
  const widgetElem = useMemo(
    () => (
      <FormControl as="div" plaintext {...field} {...otherProps}>
        {getHumanValue(field, humanValue)}
      </FormControl>
    ),
    [field, humanValue, otherProps]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      labelColumnWidth={labelColumnWidth}
      addon={addon}
      vertical={vertical}
      isCompact={isCompact}
      className={className}
    />
  )
}
ReadonlyField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  humanValue: PropTypes.any,
  isCompact: PropTypes.bool,
  labelColumnWidth: PropTypes.number
}

export const SpecialField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  children,
  extraColElem,
  addon,
  vertical,
  widget,
  isCompact,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  let { className } = widget.props
  if (validationState) {
    className = classNames(className, "is-invalid")
  }
  const widgetElem = useMemo(
    () => React.cloneElement(widget, { className, ...field, ...otherProps }),
    [className, field, otherProps, widget]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
      isCompact={isCompact}
    />
  )
}
SpecialField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  widget: PropTypes.any,
  isCompact: PropTypes.bool
}

export const customEnumButtons = list => {
  const buttons = []
  for (const key in list) {
    if (Object.prototype.hasOwnProperty.call(list, key)) {
      buttons.push({
        id: list[key].label,
        value: key,
        label: list[key].label,
        color: list[key].color
      })
    }
  }
  return buttons
}

const ButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  type,
  label,
  children,
  extraColElem,
  addon,
  vertical,
  buttons,
  enableClear,
  ...otherProps
}) => {
  const fieldLabel = label
  const widgetElem = useMemo(
    () => (
      <>
        <ToggleButtonGroup
          type={type}
          defaultValue={field.value}
          {...field}
          {...otherProps}
        >
          {buttons.map((button, index) => {
            if (!button) {
              return null
            }
            let { label, value, color, style, ...props } = button
            if (color) {
              if (
                field.value === value ||
                (Array.isArray(field.value) && field.value.includes(value))
              ) {
                style = { ...style, backgroundColor: color }
              }
              style = { ...style, borderColor: color, borderWidth: "2px" }
            }
            return (
              <ToggleButton
                {...props}
                id={`${fieldLabel ? `${fieldLabel}_` : ""}${value}`}
                key={`${fieldLabel ? `${fieldLabel}_` : ""}${value}`}
                value={value}
                style={style}
                variant="outline-secondary"
              >
                {label}
              </ToggleButton>
            )
          })}
        </ToggleButtonGroup>
        {!_isEmpty(buttons) && enableClear && (
          <RemoveButton
            title="Clear choice"
            alt="Clear choice"
            onClick={() => form.setFieldValue(field.name, "", false)}
          />
        )}
      </>
    ),
    [buttons, field, form, enableClear, otherProps, type, fieldLabel]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
    />
  )
}
ButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  type: PropTypes.string,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  buttons: PropTypes.array,
  enableClear: PropTypes.bool
}

export const RadioButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => (
  <ButtonToggleGroupField field={field} form={form} type="radio" {...props} />
)
RadioButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object
}

export const CheckboxButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => (
  <ButtonToggleGroupField
    field={field}
    form={form}
    type="checkbox"
    {...props}
  />
)
CheckboxButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object
}

export default Field

export const FieldAddon = ({ fieldId, addon }) => {
  const addonComponent = useMemo(
    () =>
      // allows passing a url for an image
      typeof addon === "string" && addon.indexOf(".") !== -1 ? (

      ) : (
        addon
      ),
    [addon]
  )
  const focusElement = useCallback(() => {
    const element = document.getElementById(fieldId)
    if (element && element.focus) {
      element.focus()
    }
  }, [fieldId])
  return <InputGroup onClick={focusElement}>{addonComponent}</InputGroup>
}
FieldAddon.propTypes = {
  fieldId: PropTypes.string,
  addon: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func,
    PropTypes.object
  ])
}

export function handleMultiSelectAddItem(newItem, onChange, curValue) {
  if (!newItem || !newItem.uuid) {
    return
  }
  if (!curValue.find(obj => obj.uuid === newItem.uuid)) {
    const value = _cloneDeep(curValue)
    value.push(_cloneDeep(newItem))
    onChange(value)
  }
}

export function handleMultiSelectRemoveItem(oldItem, onChange, curValue) {
  if (curValue.find(obj => obj.uuid === oldItem.uuid)) {
    const value = _cloneDeep(curValue)
    const index = value.findIndex(item => item.uuid === oldItem.uuid)
    value.splice(index, 1)
    onChange(value)
  }
}

export function handleSingleSelectAddItem(newItem, onChange, curValue) {
  if (!newItem || !newItem.uuid) {
    return
  }
  onChange(newItem)
}

export function handleSingleSelectRemoveItem(oldItem, onChange, curValue) {
  onChange(null)
}

export const FieldShortcuts = ({
  shortcuts,
  fieldName,
  objectType,
  curValue,
  onChange,
  handleAddItem,
  title
}) =>
  shortcuts &&
  shortcuts.length > 0 && (
    <div id={`${fieldName}-shortcut-list`} className="shortcut-list">

{title}

      {objectType.map(shortcuts, (shortcut, idx) => (
        <Button
          key={shortcut.uuid}
          variant="link"
          onClick={() => handleAddItem(shortcut, onChange, curValue)}
        >
          Add{" "}
          <LinkTo
            modelType={objectType.resourceName}
            model={shortcut}
            isLink={false}
            forShortcut
          />
        </Button>
      ))}

  )

FieldShortcuts.propTypes = {
  shortcuts: PropTypes.arrayOf(PropTypes.shape({ uuid: PropTypes.string })),
  fieldName: PropTypes.string.isRequired,
  objectType: PropTypes.func.isRequired,
  curValue: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  onChange: PropTypes.func,
  handleAddItem: PropTypes.func.isRequired,
  title: PropTypes.string.isRequired
}
<commit_msg>NCI-Agency/anet#3698: Simplify `id` and `key`<commit_after>import classNames from "classnames"
import { CompactRow } from "components/Compact"
import LinkTo from "components/LinkTo"
import RemoveButton from "components/RemoveButton"
import _cloneDeep from "lodash/cloneDeep"
import _get from "lodash/get"
import _isEmpty from "lodash/isEmpty"
import PropTypes from "prop-types"
import React, { useCallback, useMemo } from "react"
import {
  Button,
  Col,
  Form,
  FormControl,
  FormGroup,
  InputGroup,
  Row,
  ToggleButton,
  ToggleButtonGroup
} from "react-bootstrap"
import utils from "utils"
const getFieldId = field => field.id || field.name // name property is required

const getHumanValue = (field, humanValue) => {
  if (typeof humanValue === "function") {
    return humanValue(field.value)
  } else if (humanValue !== undefined) {
    return humanValue
  } else {
    return field.value
  }
}

const getFormGroupValidationState = (field, form) => {
  const { touched, errors } = form
  const fieldTouched = _get(touched, field.name)
  const fieldError = _get(errors, field.name)
  return { validationState: fieldTouched && fieldError, fieldError }
}

const getHelpBlock = (field, form) => {
  const { validationState, fieldError } = getFormGroupValidationState(
    field,
    form
  )
  return (
    validationState && (
      <FormControl.Feedback type="invalid">{fieldError}</FormControl.Feedback>
    )
  )
}

const FieldNoLabel = ({ field, form, widgetElem, children }) => {
  const id = getFieldId(field)
  return (
    <FormGroup id={`fg-${id}`} controlId={id}>
      {widgetElem}
      {getHelpBlock(field, form)}
      {children}
    </FormGroup>
  )
}
FieldNoLabel.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  widgetElem: PropTypes.object,
  children: PropTypes.any
}

const Field = ({
  field,
  form,
  label,
  widgetElem,
  children,
  extraColElem,
  addon,
  vertical,
  isCompact,
  extraAddon,
  labelColumnWidth,
  className
}) => {
  const id = getFieldId(field)
  const widget = useMemo(
    () =>
      !addon ? (
        widgetElem
      ) : (
        <InputGroup>
          {widgetElem}
          {extraAddon && <InputGroup.Addon>{extraAddon}</InputGroup.Addon>}
          <FieldAddon id={id} addon={addon} />
        </InputGroup>
      ),
    [addon, extraAddon, id, widgetElem]
  )

  if (label === undefined) {
    label = utils.sentenceCase(field.name) // name is a required prop of field
  }
  // setting label or extraColElem explicitly to null will completely remove these columns!
  const widgetWidth =
    12 -
    (label === null ? 0 : labelColumnWidth) -
    (extraColElem === null ? 0 : 3)
  // controlId prop of the FormGroup sets the id of the control element

  if (isCompact) {
    return (
      <CompactRow
        label={label}
        className={className}
        content={
          <>
            {widget}
            {getHelpBlock(field, form)}
            {extraColElem}
            {children}
          </>
        }
      />
    )
  }

  return (
    <FormGroup id={`fg-${id}`} controlId={id} className={className}>
      {vertical ? (
        <>

{label !== null && <Form.Label>{label}</Form.Label>}

          {widget}
          {getHelpBlock(field, form)}
          {children}
        </>
      ) : (
        <Row style={{ marginBottom: "1rem" }}>
          {label !== null && (
            <Col sm={labelColumnWidth} as={Form.Label}>
              {label}
            </Col>
          )}
          <Col sm={widgetWidth}>

              {widget}
              {getHelpBlock(field, form)}
              {children}

          </Col>
          {extraColElem && <Col sm={3} {...extraColElem.props} />}
        </Row>
      )}
    </FormGroup>
  )
}
Field.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  widgetElem: PropTypes.object,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  extraAddon: PropTypes.object,
  isCompact: PropTypes.bool,
  labelColumnWidth: PropTypes.number,
  className: PropTypes.string
}
Field.defaultProps = {
  vertical: false, // default direction of label and input = horizontal
  labelColumnWidth: 2
}

export const InputField = ({
  field, // { name, value, onChange, onBlur }
  form, // contains, touched, errors, values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  asA,
  inputType,
  children,
  extraColElem,
  addon,
  vertical,
  extraAddon,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  const widgetElem = useMemo(
    () => (
      <FormControl
        type={inputType}
        {...Object.without(field, "value")}
        value={utils.isNullOrUndefined(field.value) ? "" : field.value}
        {...otherProps}
        as={asA ?? "input"}
        isInvalid={validationState}
      />
    ),
    [field, otherProps, inputType, asA, validationState]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
      extraAddon={extraAddon}
    />
  )
}
InputField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  asA: PropTypes.string,
  inputType: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  extraAddon: PropTypes.object
}

export const InputFieldNoLabel = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  children,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  const widgetElem = useMemo(
    () => (
      <FormControl
        {...Object.without(field, "value")}
        value={utils.isNullOrUndefined(field.value) ? "" : field.value}
        {...otherProps}
        isInvalid={validationState}
      />
    ),
    [field, otherProps, validationState]
  )
  return (
    <FieldNoLabel
      field={field}
      form={form}
      widgetElem={widgetElem}
      children={children}
    />
  )
}
InputFieldNoLabel.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  children: PropTypes.any
}

export const ReadonlyField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  children,
  extraColElem,
  labelColumnWidth,
  addon,
  vertical,
  humanValue,
  isCompact,
  ...otherProps
}) => {
  const { className } = otherProps
  const widgetElem = useMemo(
    () => (
      <FormControl as="div" plaintext {...field} {...otherProps}>
        {getHumanValue(field, humanValue)}
      </FormControl>
    ),
    [field, humanValue, otherProps]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      labelColumnWidth={labelColumnWidth}
      addon={addon}
      vertical={vertical}
      isCompact={isCompact}
      className={className}
    />
  )
}
ReadonlyField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  humanValue: PropTypes.any,
  isCompact: PropTypes.bool,
  labelColumnWidth: PropTypes.number
}

export const SpecialField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  label,
  children,
  extraColElem,
  addon,
  vertical,
  widget,
  isCompact,
  ...otherProps
}) => {
  const { validationState } = getFormGroupValidationState(field, form)
  let { className } = widget.props
  if (validationState) {
    className = classNames(className, "is-invalid")
  }
  const widgetElem = useMemo(
    () => React.cloneElement(widget, { className, ...field, ...otherProps }),
    [className, field, otherProps, widget]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
      isCompact={isCompact}
    />
  )
}
SpecialField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  widget: PropTypes.any,
  isCompact: PropTypes.bool
}

export const customEnumButtons = list => {
  const buttons = []
  for (const key in list) {
    if (Object.prototype.hasOwnProperty.call(list, key)) {
      buttons.push({
        id: list[key].label,
        value: key,
        label: list[key].label,
        color: list[key].color
      })
    }
  }
  return buttons
}

const ButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  type,
  label,
  children,
  extraColElem,
  addon,
  vertical,
  buttons,
  enableClear,
  ...otherProps
}) => {
  const widgetElem = useMemo(
    () => (
      <>
        <ToggleButtonGroup
          type={type}
          defaultValue={field.value}
          {...field}
          {...otherProps}
        >
          {buttons.map((button, index) => {
            if (!button) {
              return null
            }
            let { label, value, color, style, ...props } = button
            if (color) {
              if (
                field.value === value ||
                (Array.isArray(field.value) && field.value.includes(value))
              ) {
                style = { ...style, backgroundColor: color }
              }
              style = { ...style, borderColor: color, borderWidth: "2px" }
            }
            return (
              <ToggleButton
                {...props}
                id={`${field.name}${value}`}
                key={`${field.name}${value}`}
                value={value}
                style={style}
                variant="outline-secondary"
              >
                {label}
              </ToggleButton>
            )
          })}
        </ToggleButtonGroup>
        {!_isEmpty(buttons) && enableClear && (
          <RemoveButton
            title="Clear choice"
            alt="Clear choice"
            onClick={() => form.setFieldValue(field.name, "", false)}
          />
        )}
      </>
    ),
    [buttons, field, form, enableClear, otherProps, type]
  )
  return (
    <Field
      field={field}
      form={form}
      label={label}
      widgetElem={widgetElem}
      children={children}
      extraColElem={extraColElem}
      addon={addon}
      vertical={vertical}
    />
  )
}
ButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object,
  type: PropTypes.string,
  label: PropTypes.string,
  children: PropTypes.any,
  extraColElem: PropTypes.object,
  addon: PropTypes.object,
  vertical: PropTypes.bool,
  buttons: PropTypes.array,
  enableClear: PropTypes.bool
}

export const RadioButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => (
  <ButtonToggleGroupField field={field} form={form} type="radio" {...props} />
)
RadioButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object
}

export const CheckboxButtonToggleGroupField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => (
  <ButtonToggleGroupField
    field={field}
    form={form}
    type="checkbox"
    {...props}
  />
)
CheckboxButtonToggleGroupField.propTypes = {
  field: PropTypes.object,
  form: PropTypes.object
}

export default Field

export const FieldAddon = ({ fieldId, addon }) => {
  const addonComponent = useMemo(
    () =>
      // allows passing a url for an image
      typeof addon === "string" && addon.indexOf(".") !== -1 ? (

      ) : (
        addon
      ),
    [addon]
  )
  const focusElement = useCallback(() => {
    const element = document.getElementById(fieldId)
    if (element && element.focus) {
      element.focus()
    }
  }, [fieldId])
  return <InputGroup onClick={focusElement}>{addonComponent}</InputGroup>
}
FieldAddon.propTypes = {
  fieldId: PropTypes.string,
  addon: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func,
    PropTypes.object
  ])
}

export function handleMultiSelectAddItem(newItem, onChange, curValue) {
  if (!newItem || !newItem.uuid) {
    return
  }
  if (!curValue.find(obj => obj.uuid === newItem.uuid)) {
    const value = _cloneDeep(curValue)
    value.push(_cloneDeep(newItem))
    onChange(value)
  }
}

export function handleMultiSelectRemoveItem(oldItem, onChange, curValue) {
  if (curValue.find(obj => obj.uuid === oldItem.uuid)) {
    const value = _cloneDeep(curValue)
    const index = value.findIndex(item => item.uuid === oldItem.uuid)
    value.splice(index, 1)
    onChange(value)
  }
}

export function handleSingleSelectAddItem(newItem, onChange, curValue) {
  if (!newItem || !newItem.uuid) {
    return
  }
  onChange(newItem)
}

export function handleSingleSelectRemoveItem(oldItem, onChange, curValue) {
  onChange(null)
}

export const FieldShortcuts = ({
  shortcuts,
  fieldName,
  objectType,
  curValue,
  onChange,
  handleAddItem,
  title
}) =>
  shortcuts &&
  shortcuts.length > 0 && (
    <div id={`${fieldName}-shortcut-list`} className="shortcut-list">

{title}

      {objectType.map(shortcuts, (shortcut, idx) => (
        <Button
          key={shortcut.uuid}
          variant="link"
          onClick={() => handleAddItem(shortcut, onChange, curValue)}
        >
          Add{" "}
          <LinkTo
            modelType={objectType.resourceName}
            model={shortcut}
            isLink={false}
            forShortcut
          />
        </Button>
      ))}

  )

FieldShortcuts.propTypes = {
  shortcuts: PropTypes.arrayOf(PropTypes.shape({ uuid: PropTypes.string })),
  fieldName: PropTypes.string.isRequired,
  objectType: PropTypes.func.isRequired,
  curValue: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  onChange: PropTypes.func,
  handleAddItem: PropTypes.func.isRequired,
  title: PropTypes.string.isRequired
}
<|endoftext|>


<commit_before>"use strict"

const pry = require('pryjs');

const log = require('log-colors');
const spawn = require('co');
const _ = require('lodash');

const discogs_client = require('lib/discogs_client');

const discogs_album_restorer = new (require('lib/restorers/discogs')).AlbumRestorer();
const itunes_album_restorer  = new (require('lib/restorers/itunes')).AlbumRestorer();
const deezer_album_restorer  = new (require('lib/restorers/deezer')).AlbumRestorer();

const RABBITMQ_CHANNEL = process.env['RABBITMQ_CHANNEL'];

class ItemsProcessor {

  constructor(){
    var self = this;

    spawn(function*(){

      require('workers/items_postprocessor');

      var connections = yield [
        require('lib/clients/db'),
        require('lib/clients/queue')
      ];

      self.db = connections[0];
      self.queue = connections[1];

      self.queue.on('disconnected', () => {
        log.info('disconnected from queue. exiting.');
        process.exit(0);
      });

      self.queue
      .default()
      .queue({ name: RABBITMQ_CHANNEL })
      .consume(self.process_item.bind(self), { noAck: true });
    }).catch(e => log.error(`erroron initialisation. ${e}`));

    return this;
  }

  static generate_query_string(item){

    var prepared = _.pick(Object.assign({}, item, {
      discogs: _.pick(item.discogs_data, [
        "id", "type", "resource_url", "similarity", "thumb", "country", "year"
      ]),
      sh_type: item.crawler_name,
      tags: item.merged_tags
    }, _.isUndefined(item.restorers_data.itunes) ? {} : {
      itunes: item.restorers_data.itunes,
    }, _.isUndefined(item.restorers_data.deezer) ? {} : {
      deezer: item.restorers_data.deezer,
    }), [
      "sh_key", "sh_type", "embed", "images", "title", "url",
      "badges", "discogs", "itunes", "deezer", "tags"
    ]);

    var data = _.reduce(prepared, (acc, value, key) => {
      acc.fields.push(`"${key}"`);
      acc.values.push(`'${(
        typeof value === 'string'
        ? value
        : JSON.stringify(value)
      ).replace(/'/ig, "''")}'`);
      return acc;
    }, { fields: [], values: [] })

    return `
      INSERT INTO ${item.item_table} (${data.fields.join(', ')})
      VALUES (${data.values.join(', ')});
    `;
  }

  static generate_badges(item){
    var badges = JSON.parse(item['badges']);
    var discogs_data = item.discogs_data;
    if(!discogs_data){
      badges.push('discogs-no-results');
    } else {
      if(discogs_data.similarity === 1){
        badges.push('discogs-title-exact-match');
      } else {
        var prepared_item_title = _.trim(
          item['title']
          .replace(/\(\d{4}\)/ig, '')
          .replace(/\[\d{4}\]/ig, '')
          .replace(/\d{0,4}kbps/ig, '')
          .replace(/\(\)/ig, '')
          .replace(/\[\]/ig, '')
          .replace(/(\/.*)/ig, '')
          .replace(/[^0-9a-zA-Z ]+/ig, '')
          .toLowerCase());

        var prepared_discogs_title = discogs_data['title'].replace(/[^0-9a-zA-Z ]+/ig, '').toLowerCase();
        if(prepared_item_title === prepared_discogs_title){
          badges.push('discogs-title-match-after-clean');
        } else {
          badges.push('discogs-title-doesnt-match');
        }
      }
    }
    return badges;
  }

  static generate_status(item){
    switch(true){
      case !~item.badges.indexOf('discogs-no-results') && item.discogs_data.similarity > 0.5:
      case item.restorers_data.itunes && item.restorers_data.itunes.similarity === 1:
      case item.restorers_data.deezer && item.restorers_data.deezer.similarity === 1:
        return 'good';
      default:
        return 'bad';
    }
  }

  static generate_table_name(item){
    switch(true){
      case item.status === 'good':
        return 'items';
      default:
        return 'bad_items';
    }
  }

  static process_data(item){
    return spawn(function*(){

      log.info(`got item #${item.sh_key}`);
      var processed_item = Object.assign({}, item);

      //should be filtered on spider side
      Object.assign(processed_item, {
        title: _.trim(processed_item.title)
      });

      Object.assign(processed_item, {
        discogs_data: yield discogs_album_restorer.restore(processed_item)
      });

      /* think of something better here */
      Object.assign(processed_item, {
        original_title: processed_item.title,
      }, !processed_item.discogs_data ? {} : {
        title: processed_item.discogs_data.title
      });
      /* / */

      Object.assign(processed_item, {
        restorers_data: yield {
          itunes: itunes_album_restorer.restore(processed_item),
          deezer: deezer_album_restorer.restore(processed_item)
        }
      });

      Object.assign(processed_item, {
        badges: ItemsProcessor.generate_badges(processed_item)
      });

      Object.assign(processed_item, {
        status: ItemsProcessor.generate_status(processed_item)
      });

      Object.assign(processed_item, {
        item_table: ItemsProcessor.generate_table_name(processed_item)
      });

      Object.assign(processed_item, {
        merged_tags: _.uniq(
          JSON.parse(processed_item.tags).concat((processed_item.discogs_data || {genre: []}).genre)
                                         .concat((processed_item.discogs_data || {style: []}).style)
        )
      });

      return processed_item;

    }).catch(e => log.error(`error while processing item. ${e}`));
  }

  process_item(item) {
    return ItemsProcessor.process_data(item).then((processed_item) => {
      var query_string = ItemsProcessor.generate_query_string(processed_item);

      if(process.env['NODE_ENV'] !== 'production'){
        log.info(query_string);
      } else {
        new Promise((resolve) => {
          this.db.run(query_string, (err, items) => {
            if(err){ log.error(err); }
            log.info(`item added to table ${processed_item.item_table}`);
            resolve(items);
          });
        });
      }

      process.emit('postprocess', Object.assign({}, processed_item));
    }).catch(e => log.error(e))
  }
}

//run worker
if(process.env['NODE_ENV'] !== 'test'){
  new ItemsProcessor();
}

module.exports = ItemsProcessor;

<commit_msg>feat(string analyser): apply strings parser for discogs<commit_after>"use strict"

const pry = require('pryjs');

const log = require('log-colors');
const spawn = require('co');
const _ = require('lodash');

const string_parser = require('lib/string_parser');

const discogs_client = require('lib/discogs_client');

const discogs_album_restorer = new (require('lib/restorers/discogs')).AlbumRestorer();
const itunes_album_restorer  = new (require('lib/restorers/itunes')).AlbumRestorer();
const deezer_album_restorer  = new (require('lib/restorers/deezer')).AlbumRestorer();

const RABBITMQ_CHANNEL = process.env['RABBITMQ_CHANNEL'];

class ItemsProcessor {

  constructor(){
    var self = this;

    spawn(function*(){

      require('workers/items_postprocessor');

      var connections = yield [
        require('lib/clients/db'),
        require('lib/clients/queue')
      ];

      self.db = connections[0];
      self.queue = connections[1];

      log.info('pulling masks');
      var masks_query = `
        SELECT mask, occurencies, (occurencies::FLOAT / total::float) * 100 AS weight FROM (
              SELECT
                  recognition_result->'title'->>'mask'::text AS mask,
                  count(*) AS occurencies
              FROM recognition_masks
              GROUP BY mask
              ORDER BY occurencies DESC
          ) a, (
              SELECT count(*) AS total FROM recognition_masks
          ) b
        WHERE occurencies > 1
        ORDER BY occurencies desc, char_length(mask) desc
      `;

      self.masks = yield new Promise((resolve) => self.db.run(masks_query, (err, items) => {
        if(err){ reject(err); }
        resolve(items);
      }));
      log.info('masks pulled');

      self.queue.on('disconnected', () => {
        log.info('disconnected from queue. exiting.');
        process.exit(0);
      });

      self.queue
      .default()
      .queue({ name: RABBITMQ_CHANNEL })
      .consume(self.process_item.bind(self), { noAck: true });
    }).catch(e => log.error(`erroron initialisation. ${e}`));

    return this;
  }

  static generate_query_string(item){

    var prepared = _.pick(Object.assign({}, item, {
      discogs: _.pick(item.discogs_data, [
        "id", "type", "resource_url", "similarity", "thumb", "country", "year"
      ]),
      sh_type: item.crawler_name,
      tags: item.merged_tags
    }, _.isUndefined(item.restorers_data.itunes) ? {} : {
      itunes: item.restorers_data.itunes,
    }, _.isUndefined(item.restorers_data.deezer) ? {} : {
      deezer: item.restorers_data.deezer,
    }), [
      "sh_key", "sh_type", "embed", "images", "title", "url",
      "badges", "discogs", "itunes", "deezer", "tags"
    ]);

    var data = _.reduce(prepared, (acc, value, key) => {
      acc.fields.push(`"${key}"`);
      acc.values.push(`'${(
        typeof value === 'string'
        ? value
        : JSON.stringify(value)
      ).replace(/'/ig, "''")}'`);
      return acc;
    }, { fields: [], values: [] })

    return `
      INSERT INTO ${item.item_table} (${data.fields.join(', ')})
      VALUES (${data.values.join(', ')});
    `;
  }

  static generate_badges(item){
    var badges = JSON.parse(item['badges']);
    var discogs_data = item.discogs_data;
    if(!discogs_data){
      badges.push('discogs-no-results');
    } else {
      if(discogs_data.similarity === 1){
        badges.push('discogs-title-exact-match');
      } else {
        var prepared_item_title = _.trim(
          item['title']
          .replace(/\(\d{4}\)/ig, '')
          .replace(/\[\d{4}\]/ig, '')
          .replace(/\d{0,4}kbps/ig, '')
          .replace(/\(\)/ig, '')
          .replace(/\[\]/ig, '')
          .replace(/(\/.*)/ig, '')
          .replace(/[^0-9a-zA-Z ]+/ig, '')
          .toLowerCase());

        var prepared_discogs_title = discogs_data['title'].replace(/[^0-9a-zA-Z ]+/ig, '').toLowerCase();
        if(prepared_item_title === prepared_discogs_title){
          badges.push('discogs-title-match-after-clean');
        } else {
          badges.push('discogs-title-doesnt-match');
        }
      }
    }
    return badges;
  }

  static generate_status(item){
    switch(true){
      case !~item.badges.indexOf('discogs-no-results') && item.discogs_data.similarity > 0.5:
      case item.restorers_data.itunes && item.restorers_data.itunes.similarity === 1:
      case item.restorers_data.deezer && item.restorers_data.deezer.similarity === 1:
        return 'good';
      default:
        return 'bad';
    }
  }

  static generate_table_name(item){
    switch(true){
      case item.status === 'good':
        return 'items';
      default:
        return 'bad_items';
    }
  }

  static process_data(item, masks){
    //1) passing masks as an argument is probably not a good idea
    //2) use all restorers in parallel
    return spawn(function*(){

      log.info(`got item #${item.sh_key}`);
      var processed_item = Object.assign({}, item);

      //should be filtered on spider side
      Object.assign(processed_item, {
        title: _.trim(processed_item.title)
      });

      Object.assign(processed_item, {
        discogs_data: yield discogs_album_restorer.restore(processed_item)
      });

      if(processed_item.discogs_data === null){
        var title_variants = string_parser.parse_massive(processed_item.title, masks).slice(0, 2);
        for(var variant of title_variants.slice(0, 1)){
          var discogs_data = yield discogs_album_restorer.restore(Object.assign({}, processed_item, {
            title: `${variant.artist} - ${variant.album}`
          }));
          if(discogs_data !== null){
            Object.assign(processed_item, {
              discogs_data: discogs_data
            });
            break;
          }
        }
      }

      /* think of something better here */
      Object.assign(processed_item, {
        original_title: processed_item.title,
      }, !processed_item.discogs_data ? {} : {
        title: processed_item.discogs_data.title
      });
      /* / */

      Object.assign(processed_item, {
        restorers_data: yield {
          itunes: itunes_album_restorer.restore(processed_item),
          deezer: deezer_album_restorer.restore(processed_item)
        }
      });

      Object.assign(processed_item, {
        badges: ItemsProcessor.generate_badges(processed_item)
      });

      Object.assign(processed_item, {
        status: ItemsProcessor.generate_status(processed_item)
      });

      Object.assign(processed_item, {
        item_table: ItemsProcessor.generate_table_name(processed_item)
      });

      Object.assign(processed_item, {
        merged_tags: _.uniq(
          JSON.parse(processed_item.tags).concat((processed_item.discogs_data || {genre: []}).genre)
                                         .concat((processed_item.discogs_data || {style: []}).style)
        )
      });

      return processed_item;

    }).catch(e => log.error(`error while processing item. ${e}`));
  }

  process_item(item) {
    return ItemsProcessor.process_data(item, this.masks).then((processed_item) => {
      var query_string = ItemsProcessor.generate_query_string(processed_item);

      if(process.env['NODE_ENV'] !== 'production'){
        log.info(query_string);
      } else {
        new Promise((resolve) => {
          this.db.run(query_string, (err, items) => {
            if(err){ log.error(err); }
            log.info(`item added to table ${processed_item.item_table}`);
            resolve(items);
          });
        });
      }

      process.emit('postprocess', Object.assign({}, processed_item));
    }).catch(e => log.error(e))
  }
}

//run worker
if(process.env['NODE_ENV'] !== 'test'){
  new ItemsProcessor();
}

module.exports = ItemsProcessor;

<|endoftext|>


<commit_before>import React from 'react';
import Node from './Node';
import NodeContainer from './NodeContainer';

export const NodeList = ({ nodes, offset, zoom, renderNode }) => {
  const NodeComponent = renderNode || Node;

  console.debug("NodeList - nodes prop:", nodes)
  console.debug('NodeList - NodeComponent', NodeComponent);

  return (
    <React.Fragment>
      {nodes.map(node => (
        <NodeContainer key={node._id} node={node} offset={offset} zoom={zoom}>
          <NodeComponent {...deriveProps(node, offset)} />
        </NodeContainer>
      ))}
    </React.Fragment>
  );
};

NodeList.defaultProps = {
  zoom: 1,
  offset: { x: 0, y: 0 },
}

const deriveProps = (node) => ({
  wrapper: node,
  gui: node.gui,
  inputs: node.inputs,
  values: node.values,
  onChange: node.onChange,
  title: node.cfg.title,
  width: node.cfg.width,
  bgColor: node.cfg.fill,
  ports: node.cfg.ports || {},
});

export default NodeList;
<commit_msg>Improve customNode debug info<commit_after>import React from 'react';
import Node from './Node';
import NodeContainer from './NodeContainer';

export const NodeList = ({ nodes, offset, zoom, renderNode }) => {
  console.debug(`NodeList - customNode: ${renderNode != null}`)
  console.debug("NodeList - nodes prop:", nodes)
  const NodeComponent = renderNode || Node;

  return (
    <React.Fragment>
      {nodes.map(node => (
        <NodeContainer key={node._id} node={node} offset={offset} zoom={zoom}>
          <NodeComponent {...deriveProps(node, offset)} />
        </NodeContainer>
      ))}
    </React.Fragment>
  );
};

NodeList.defaultProps = {
  zoom: 1,
  offset: { x: 0, y: 0 },
}

const deriveProps = (node) => ({
  wrapper: node,
  gui: node.gui,
  inputs: node.inputs,
  values: node.values,
  onChange: node.onChange,
  title: node.cfg.title,
  width: node.cfg.width,
  bgColor: node.cfg.fill,
  ports: node.cfg.ports || {},
});

export default NodeList;
<|endoftext|>


<commit_before>import { CLEAR_GRAPHS, SELECT_APP, SELECT_POOL } from '../actions/types';
import { data } from '../data';

function instanceByApp (app) {
  switch (app) {
    case 'app1':
      return ["00", "01"];
    case 'app2': case 'app3':
      return ["00","01","02","03","04","05"];
  }
  return [];
}

export default function (state=[], action) {
  switch (action.type) {
  case CLEAR_GRAPHS:
    return [];
  case SELECT_APP:
    return instanceByApp(action.payload);
  case SELECT_POOL:
    switch (action.payload.pool) {
      case 'pre': case 'test':
        return [];
      default:
        return instanceByApp(action.payload.app);
    }
  }

  return state;
}
<commit_msg>Remove trailing chars<commit_after>import { CLEAR_GRAPHS, SELECT_APP, SELECT_POOL } from '../actions/types';
import { data } from '../data';

function instanceByApp (app) {
  switch (app) {
    case 'app1':
      return ["00", "01"];
    case 'app2': case 'app3':
      return ["00","01","02","03","04","05"];
  }
  return [];
}

export default function (state=[], action) {
  switch (action.type) {
  case CLEAR_GRAPHS:
    return [];
  case SELECT_APP:
    return instanceByApp(action.payload);
  case SELECT_POOL:
    switch (action.payload.pool) {
      case 'pre': case 'test':
        return [];
      default:
        return instanceByApp(action.payload.app);
    }
  }

  return state;
}
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * an object to manage animation
     * @class
     * @extends me.Sprite
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinates of the sprite object
     * @param {Number} y the y coordinates of the sprite object
     * @param {Object} settings Contains additional parameters for the animation sheet
     * @param {Image|String} settings.image Image to use for the animation
     * @param {Number} [settings.framewidth] Width of a single frame within the spritesheet
     * @param {Number} [settings.frameheight] Height of a single frame within the spritesheet
     * @example
     * // standalone image
     * var animationSheet = new me.AnimationSheet(0, 0, {
     *     image : "animationsheet",
     *     framewidth : 64,
     *     frameheight : 64
     * });
     */
    me.AnimationSheet = me.Sprite.extend(
    /** @scope me.AnimationSheet.prototype */
    {
        /** @ignore */
        init : function (x, y, settings) {

            /**
             * pause and resume animation

             * default value : false;
             * @public
             * @type Boolean
             * @name me.AnimationSheet#animationpause
             */
            this.animationpause = false;

            /**
             * animation cycling speed (delay between frame in ms)

             * default value : 100ms;
             * @public
             * @type Number
             * @name me.AnimationSheet#animationspeed
             */
            this.animationspeed = 100;

            // hold all defined animation
            this.anim = {};

            // a flag to reset animation
            this.resetAnim = null;

            // default animation sequence
            this.current = null;

            // animation frame delta
            this.dt = 0;

            // default animation speed (ms)
            this.animationspeed = 100;

            // call the constructor
            this._super(me.Sprite, "init", [ x, y, settings ]);

            // store/reset the current atlas information
            if (typeof(settings.atlas) !== "undefined") {
                this.textureAtlas = settings.atlas;
                this.atlasIndices = settings.atlasIndices;
            } else {
                // "regular" spritesheet
                this.textureAtlas = me.video.renderer.cache.get(
                    me.utils.getImage(settings.image),
                    settings
                ).getAtlas();
                this.atlasIndices = null;
            }

            // create a default animation sequence with all sprites
            this.addAnimation("default", null);

            // set as default
            this.setCurrentAnimation("default");
        },

        /**
         * add an animation

         * For fixed-sized cell sprite sheet, the index list must follow the
         * logic as per the following example :

         *
         * @name addAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @param {Number[]|String[]} index list of sprite index or name
         * defining the animation. Can also use objects to specify delay for each frame, see below
         * @param {Number} [animationspeed] cycling speed for animation in ms
         * (delay between each frame).
         * @see me.AnimationSheet#animationspeed
         * @example
         * // walking animation
         * this.addAnimation("walk", [ 0, 1, 2, 3, 4, 5 ]);
         * // eating animation
         * this.addAnimation("eat", [ 6, 6 ]);
         * // rolling animation
         * this.addAnimation("roll", [ 7, 8, 9, 10 ]);
         * // slower animation
         * this.addAnimation("roll", [ 7, 8, 9, 10 ], 200);
         * // or get more specific with delay for each frame. Good solution instead of repeating:
         * this.addAnimation("turn", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }])
         * // can do this with atlas values as well:
         * this.addAnimation("turn", [{ name: "turnone", delay: 200 }, { name: "turntwo", delay: 100 }])
         */
        addAnimation : function (name, index, animationspeed) {
            this.anim[name] = {
                name : name,
                frames : [],
                idx : 0,
                length : 0
            };

            if (index == null) {
                index = [];
                // create a default animation with all frame
                Object.keys(this.textureAtlas).forEach(function (v, i) {
                    index[i] = i;
                });
            }

            // # of frames
            var counter = 0;
            // set each frame configuration (offset, size, etc..)
            for (var i = 0, len = index.length; i < len; i++) {
                var frameObject = index[i];
                if (typeof(frameObject) === "number" || typeof(frameObject) === "string") {
                  frameObject = {
                    name: frameObject,
                    delay: animationspeed || this.animationspeed
                  };
                }
                var frameObjectName = frameObject.name;
                if (typeof(frameObjectName) === "number") {
                    if (typeof (this.textureAtlas[frameObjectName]) !== "undefined") {
                        // TODO: adding the cache source coordinates add undefined entries in webGL mode
                        this.anim[name].frames[i] = Object.assign(
                          {},
                          this.textureAtlas[frameObjectName],
                          frameObject
                        );
                        counter++;
                    }
                } else { // string
                    if (this.atlasIndices === null) {
                        throw new me.Renderable.Error(
                            "string parameters for addAnimation are not allowed for standard spritesheet based Texture"
                        );
                    } else {
                        this.anim[name].frames[i] = Object.assign(
                          {},
                          this.textureAtlas[this.atlasIndices[frameObjectName]],
                          frameObject
                        );
                        counter++;
                    }
                }
            }
            this.anim[name].length = counter;
        },

        /**
         * set the current animation
         * this will always change the animation & set the frame to zero
         * @name setCurrentAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @param {String|Function} [onComplete] animation id to switch to when
         * complete, or callback
         * @example
         * // set "walk" animation
         * this.setCurrentAnimation("walk");
         *
         * // set "walk" animation if it is not the current animation
         * if (this.isCurrentAnimation("walk")) {
         *     this.setCurrentAnimation("walk");
         * }
         *
         * // set "eat" animation, and switch to "walk" when complete
         * this.setCurrentAnimation("eat", "walk");
         *
         * // set "die" animation, and remove the object when finished
         * this.setCurrentAnimation("die", (function () {
         *    me.game.world.removeChild(this);
         *    return false; // do not reset to first frame
         * }).bind(this));
         *
         * // set "attack" animation, and pause for a short duration
         * this.setCurrentAnimation("die", (function () {
         *    this.animationpause = true;
         *
         *    // back to "standing" animation after 1 second
         *    setTimeout(function () {
         *        this.setCurrentAnimation("standing");
         *    }, 1000);
         *
         *    return false; // do not reset to first frame
         * }).bind(this));
         **/
        setCurrentAnimation : function (name, resetAnim, _preserve_dt) {
            if (this.anim[name]) {
                this.current = this.anim[name];
                this.resetAnim = resetAnim || null;
                this.setAnimationFrame(this.current.idx);
                if (!_preserve_dt) {
                    this.dt = 0;
                }
            } else {
                throw new me.Renderable.Error("animation id '" + name + "' not defined");
            }
        },

        /**
         * return true if the specified animation is the current one.
         * @name isCurrentAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @return {Boolean}
         * @example
         * if (!this.isCurrentAnimation("walk")) {
         *     // do something funny...
         * }
         */
        isCurrentAnimation : function (name) {
            return this.current.name === name;
        },

        /**
         * force the current animation frame index.
         * @name setAnimationFrame
         * @memberOf me.AnimationSheet
         * @function
         * @param {Number} [index=0] animation frame index
         * @example
         * // reset the current animation to the first frame
         * this.setAnimationFrame();
         */
        setAnimationFrame : function (idx) {
            this.current.idx = (idx || 0) % this.current.length;
            var frame = this.getAnimationFrameObjectByIndex(this.current.idx);
            this.offset = frame.offset;
            this.width = frame.width;
            this.height = frame.height;
            this._sourceAngle = frame.angle;
        },

        /**
         * return the current animation frame index.
         * @name getCurrentAnimationFrame
         * @memberOf me.AnimationSheet
         * @function
         * @return {Number} current animation frame index
         */
        getCurrentAnimationFrame : function () {
            return this.current.idx;
        },

        /**
         * Returns the frame object by the index.
         * @name getAnimationFrameObjectByIndex
         * @memberOf me.AnimationSheet
         * @function
         * @private
         * @return {Number} if using number indices. Returns {Object} containing frame data if using texture atlas
         */
        getAnimationFrameObjectByIndex : function (id) {
            return this.current.frames[id];
        },

        /**
         * update the animation

         * this is automatically called by the game manager {@link me.game}
         * @name update
         * @memberOf me.AnimationSheet
         * @function
         * @protected
         * @param {Number} dt time since the last update in milliseconds.
         */
        update : function (dt) {
            // Update animation if necessary
            if (this.animationpause || this.current.length <= 1) {
                return this._super(me.Sprite, "update", [ dt ]);
            }

            var duration = 0,
                result = false;

            this.dt += dt;
            duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
            while (this.dt >= duration) {
                result = true;
                this.dt -= duration;
                this.setAnimationFrame(this.current.idx + 1);

                // Switch animation if we reach the end of the strip and a callback is defined
                if (this.current.idx === 0 && this.resetAnim)  {
                    // If string, change to the corresponding animation
                    if (typeof this.resetAnim === "string") {
                        this.setCurrentAnimation(this.resetAnim, null, true);
                    }
                    // Otherwise is must be callable
                    else if (this.resetAnim() === false) {
                        // Reset to last frame
                        this.setAnimationFrame(this.current.length - 1);

                        // Bail early without skipping any more frames.
                        this.dt %= duration;
                        break;
                    }
                }

                // Get next frame duration
                duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
            }

            return this._super(me.Sprite, "update", [ dt ]) || result;
        }
    });
})();
<commit_msg>fixed indentation<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * an object to manage animation
     * @class
     * @extends me.Sprite
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinates of the sprite object
     * @param {Number} y the y coordinates of the sprite object
     * @param {Object} settings Contains additional parameters for the animation sheet
     * @param {Image|String} settings.image Image to use for the animation
     * @param {Number} [settings.framewidth] Width of a single frame within the spritesheet
     * @param {Number} [settings.frameheight] Height of a single frame within the spritesheet
     * @example
     * // standalone image
     * var animationSheet = new me.AnimationSheet(0, 0, {
     *     image : "animationsheet",
     *     framewidth : 64,
     *     frameheight : 64
     * });
     */
    me.AnimationSheet = me.Sprite.extend(
    /** @scope me.AnimationSheet.prototype */
    {
        /** @ignore */
        init : function (x, y, settings) {

            /**
             * pause and resume animation

             * default value : false;
             * @public
             * @type Boolean
             * @name me.AnimationSheet#animationpause
             */
            this.animationpause = false;

            /**
             * animation cycling speed (delay between frame in ms)

             * default value : 100ms;
             * @public
             * @type Number
             * @name me.AnimationSheet#animationspeed
             */
            this.animationspeed = 100;

            // hold all defined animation
            this.anim = {};

            // a flag to reset animation
            this.resetAnim = null;

            // default animation sequence
            this.current = null;

            // animation frame delta
            this.dt = 0;

            // default animation speed (ms)
            this.animationspeed = 100;

            // call the constructor
            this._super(me.Sprite, "init", [ x, y, settings ]);

            // store/reset the current atlas information
            if (typeof(settings.atlas) !== "undefined") {
                this.textureAtlas = settings.atlas;
                this.atlasIndices = settings.atlasIndices;
            } else {
                // "regular" spritesheet
                this.textureAtlas = me.video.renderer.cache.get(
                    me.utils.getImage(settings.image),
                    settings
                ).getAtlas();
                this.atlasIndices = null;
            }

            // create a default animation sequence with all sprites
            this.addAnimation("default", null);

            // set as default
            this.setCurrentAnimation("default");
        },

        /**
         * add an animation

         * For fixed-sized cell sprite sheet, the index list must follow the
         * logic as per the following example :

         *
         * @name addAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @param {Number[]|String[]|Object[]} index list of sprite index or name
         * defining the animation. Can also use objects to specify delay for each frame, see below
         * @param {Number} [animationspeed] cycling speed for animation in ms
         * (delay between each frame).
         * @see me.AnimationSheet#animationspeed
         * @example
         * // walking animation
         * this.addAnimation("walk", [ 0, 1, 2, 3, 4, 5 ]);
         * // eating animation
         * this.addAnimation("eat", [ 6, 6 ]);
         * // rolling animation
         * this.addAnimation("roll", [ 7, 8, 9, 10 ]);
         * // slower animation
         * this.addAnimation("roll", [ 7, 8, 9, 10 ], 200);
         * // or get more specific with delay for each frame. Good solution instead of repeating:
         * this.addAnimation("turn", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }])
         * // can do this with atlas values as well:
         * this.addAnimation("turn", [{ name: "turnone", delay: 200 }, { name: "turntwo", delay: 100 }])
         */
        addAnimation : function (name, index, animationspeed) {
            this.anim[name] = {
                name : name,
                frames : [],
                idx : 0,
                length : 0
            };

            if (index == null) {
                index = [];
                // create a default animation with all frame
                Object.keys(this.textureAtlas).forEach(function (v, i) {
                    index[i] = i;
                });
            }

            // # of frames
            var counter = 0;
            // set each frame configuration (offset, size, etc..)
            for (var i = 0, len = index.length; i < len; i++) {
                var frameObject = index[i];
                if (typeof(frameObject) === "number" || typeof(frameObject) === "string") {
                    frameObject = {
                        name: frameObject,
                        delay: animationspeed || this.animationspeed
                    };
                }
                var frameObjectName = frameObject.name;
                if (typeof(frameObjectName) === "number") {
                    if (typeof (this.textureAtlas[frameObjectName]) !== "undefined") {
                        // TODO: adding the cache source coordinates add undefined entries in webGL mode
                        this.anim[name].frames[i] = Object.assign(
                            {},
                            this.textureAtlas[frameObjectName],
                            frameObject
                        );
                        counter++;
                    }
                } else { // string
                    if (this.atlasIndices === null) {
                        throw new me.Renderable.Error(
                            "string parameters for addAnimation are not allowed for standard spritesheet based Texture"
                        );
                    } else {
                        this.anim[name].frames[i] = Object.assign(
                            {},
                            this.textureAtlas[this.atlasIndices[frameObjectName]],
                            frameObject
                        );
                        counter++;
                    }
                }
            }
            this.anim[name].length = counter;
        },

        /**
         * set the current animation
         * this will always change the animation & set the frame to zero
         * @name setCurrentAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @param {String|Function} [onComplete] animation id to switch to when
         * complete, or callback
         * @example
         * // set "walk" animation
         * this.setCurrentAnimation("walk");
         *
         * // set "walk" animation if it is not the current animation
         * if (this.isCurrentAnimation("walk")) {
         *     this.setCurrentAnimation("walk");
         * }
         *
         * // set "eat" animation, and switch to "walk" when complete
         * this.setCurrentAnimation("eat", "walk");
         *
         * // set "die" animation, and remove the object when finished
         * this.setCurrentAnimation("die", (function () {
         *    me.game.world.removeChild(this);
         *    return false; // do not reset to first frame
         * }).bind(this));
         *
         * // set "attack" animation, and pause for a short duration
         * this.setCurrentAnimation("die", (function () {
         *    this.animationpause = true;
         *
         *    // back to "standing" animation after 1 second
         *    setTimeout(function () {
         *        this.setCurrentAnimation("standing");
         *    }, 1000);
         *
         *    return false; // do not reset to first frame
         * }).bind(this));
         **/
        setCurrentAnimation : function (name, resetAnim, _preserve_dt) {
            if (this.anim[name]) {
                this.current = this.anim[name];
                this.resetAnim = resetAnim || null;
                this.setAnimationFrame(this.current.idx);
                if (!_preserve_dt) {
                    this.dt = 0;
                }
            } else {
                throw new me.Renderable.Error("animation id '" + name + "' not defined");
            }
        },

        /**
         * return true if the specified animation is the current one.
         * @name isCurrentAnimation
         * @memberOf me.AnimationSheet
         * @function
         * @param {String} name animation id
         * @return {Boolean}
         * @example
         * if (!this.isCurrentAnimation("walk")) {
         *     // do something funny...
         * }
         */
        isCurrentAnimation : function (name) {
            return this.current.name === name;
        },

        /**
         * force the current animation frame index.
         * @name setAnimationFrame
         * @memberOf me.AnimationSheet
         * @function
         * @param {Number} [index=0] animation frame index
         * @example
         * // reset the current animation to the first frame
         * this.setAnimationFrame();
         */
        setAnimationFrame : function (idx) {
            this.current.idx = (idx || 0) % this.current.length;
            var frame = this.getAnimationFrameObjectByIndex(this.current.idx);
            this.offset = frame.offset;
            this.width = frame.width;
            this.height = frame.height;
            this._sourceAngle = frame.angle;
        },

        /**
         * return the current animation frame index.
         * @name getCurrentAnimationFrame
         * @memberOf me.AnimationSheet
         * @function
         * @return {Number} current animation frame index
         */
        getCurrentAnimationFrame : function () {
            return this.current.idx;
        },

        /**
         * Returns the frame object by the index.
         * @name getAnimationFrameObjectByIndex
         * @memberOf me.AnimationSheet
         * @function
         * @private
         * @return {Number} if using number indices. Returns {Object} containing frame data if using texture atlas
         */
        getAnimationFrameObjectByIndex : function (id) {
            return this.current.frames[id];
        },

        /**
         * update the animation

         * this is automatically called by the game manager {@link me.game}
         * @name update
         * @memberOf me.AnimationSheet
         * @function
         * @protected
         * @param {Number} dt time since the last update in milliseconds.
         */
        update : function (dt) {
            // Update animation if necessary
            if (this.animationpause || this.current.length <= 1) {
                return this._super(me.Sprite, "update", [ dt ]);
            }

            var duration = 0,
                result = false;

            this.dt += dt;
            duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
            while (this.dt >= duration) {
                result = true;
                this.dt -= duration;
                this.setAnimationFrame(this.current.idx + 1);

                // Switch animation if we reach the end of the strip and a callback is defined
                if (this.current.idx === 0 && this.resetAnim)  {
                    // If string, change to the corresponding animation
                    if (typeof this.resetAnim === "string") {
                        this.setCurrentAnimation(this.resetAnim, null, true);
                    }
                    // Otherwise is must be callable
                    else if (this.resetAnim() === false) {
                        // Reset to last frame
                        this.setAnimationFrame(this.current.length - 1);

                        // Bail early without skipping any more frames.
                        this.dt %= duration;
                        break;
                    }
                }

                // Get next frame duration
                duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
            }

            return this._super(me.Sprite, "update", [ dt ]) || result;
        }
    });
})();
<|endoftext|>


<commit_before>import webpack from 'webpack'
import { logError, log } from '../util/log'
import Server from 'webpack-dev-server'

/**
 * Development server
 */
export default (saguiOptions) => new Promise((resolve, reject) => {
  const options = {
    hot: true,
    historyApiFallback: saguiOptions.pages && saguiOptions.pages[0] && `${saguiOptions.pages[0]}.html`,
    ...saguiOptions.develop
  }

  try {
    new Server(webpack(setupHMR(saguiOptions).webpack), options).listen(saguiOptions.port, '0.0.0.0', (err) => {
      if (err) {
        logError(`Server failed to started at http://localhost:${saguiOptions.port}`)
        reject(err)
      } else {
        log(`Server started at http://localhost:${saguiOptions.port}/webpack-dev-server/`)
      }
    })
  } catch (e) {
    reject(e)
  }
})

/**
 * HMR bundle setup based on code from
 * https://github.com/webpack/webpack-dev-server/blob/master/bin/webpack-dev-server.js
 */
function setupHMR (saguiOptions) {
  return {
    ...saguiOptions,
    webpack: saguiOptions.webpack.map((webpack) => ({
      ...webpack,
      entry: concatHMRBundle(saguiOptions, webpack.entry)
    }))
  }
}

function concatHMRBundle (saguiOptions, entry) {
  const devClient = [
    require.resolve('webpack-dev-server/client/') + '?http://0.0.0.0:' + saguiOptions.port,
    'webpack/hot/dev-server'
  ]

  if (typeof entry === 'object' && !Array.isArray(entry)) {
    return Object.keys(entry).reduce((entries, key) => ({
      ...entries,
      [key]: devClient.concat(entry[key])
    }), {})
  } else {
    return devClient.concat(entry)
  }
}
<commit_msg>Fixed the bug where exposed port differs from the internal port which sagui is listening on<commit_after>import webpack from 'webpack'
import { logError, log } from '../util/log'
import Server from 'webpack-dev-server'

/**
 * Development server
 */
export default (saguiOptions) => new Promise((resolve, reject) => {
  const options = {
    hot: true,
    historyApiFallback: saguiOptions.pages && saguiOptions.pages[0] && `${saguiOptions.pages[0]}.html`,
    ...saguiOptions.develop
  }

  try {
    new Server(webpack(setupHMR(saguiOptions).webpack), options).listen(saguiOptions.port, '0.0.0.0', (err) => {
      if (err) {
        logError(`Server failed to started at http://localhost:${saguiOptions.port}`)
        reject(err)
      } else {
        log(`Server started at http://localhost:${saguiOptions.port}/webpack-dev-server/`)
      }
    })
  } catch (e) {
    reject(e)
  }
})

/**
 * HMR bundle setup based on code from
 * https://github.com/webpack/webpack-dev-server/blob/master/bin/webpack-dev-server.js
 */
function setupHMR (saguiOptions) {
  return {
    ...saguiOptions,
    webpack: saguiOptions.webpack.map((webpack) => ({
      ...webpack,
      entry: concatHMRBundle(saguiOptions, webpack.entry)
    }))
  }
}

function concatHMRBundle (saguiOptions, entry) {
  const devClient = [
    require.resolve('webpack-dev-server/client/') + '?/',
    'webpack/hot/dev-server'
  ]

  if (typeof entry === 'object' && !Array.isArray(entry)) {
    return Object.keys(entry).reduce((entries, key) => ({
      ...entries,
      [key]: devClient.concat(entry[key])
    }), {})
  } else {
    return devClient.concat(entry)
  }
}
<|endoftext|>


<commit_before>import React from 'react';

import Link from 'gatsby-link';

import Page from '../ui/Page';
import Footer from '../ui/Footer';
import Wrap from '../ui/Wrap';
import LinkButton from '../ui/LinkButton';
import GitHubButton from '../ui/GitHubButton';
import CodeBlock from '../ui/CodeBlock';

import styles from './index.module.scss';

import FeatureModifiers from '../code-samples/examples/modifiers-styles';
import FeatureInput from '../code-samples/examples/input';
import FeatureInput2 from '../code-samples/examples/input-from-to';
import FeatureLocalization from '../code-samples/examples/localization';
import FeatureRange from '../code-samples/examples/selected-range';

import imgLogo from '../images/logo-icon.png';

export default function HomePage() {
  return (
    <Page>

        <Wrap>

react-day-picker

            Date picker component for{' '}
            React

            Flexible, highly customizable, localizable, with ARIA support, no
            external dependencies, ~9KB gzipped

            <LinkButton to="/docs">Read the docs</LinkButton>
            <LinkButton to="/examples">Browse the examples</LinkButton>

          <GitHubButton />
        </Wrap>

      <Wrap>

Style days with modifiers

              Define the disabled or selected days and change the aspect of each
              day cell with <Link to="/docs/matching-days">modifiers</Link>.

              <FeatureModifiers />

Works with input fields

              Display the date picker in an overlay using the{' '}
              <Link to="/docs/input">DayPickerInput</Link> component.

              <FeatureInput />

...or choose a range of days:

              <FeatureInput2 />

Localizable

              Use your own translation strings or import those from moment.js,
              if you use it.

              <FeatureLocalization />

Select range of days

              Specify which days should be selected in your component’s state (<Link to="/examples/selected-range">
                source
              </Link>).

              <FeatureRange numberOfMonths={1} />

Get started

Install via npm or yarn

            <CodeBlock language="bash">
              {`$ npm install react-day-picker --save
# or with yarn:
$ yarn add react-day-picker`}
            </CodeBlock>

              {' '}

              {' '}

              {' '}

Import in your component

            <CodeBlock>{`import React from 'react';
import DayPicker from 'react-day-picker';
import 'react-day-picker/lib/style.css';

export default function Hello() {
  return <DayPicker />;
}
`}</CodeBlock>

      </Wrap>

    </Page>
  );
}
<commit_msg>Add link to bundlephobia<commit_after>import React from 'react';

import Link from 'gatsby-link';

import Page from '../ui/Page';
import Footer from '../ui/Footer';
import Wrap from '../ui/Wrap';
import LinkButton from '../ui/LinkButton';
import GitHubButton from '../ui/GitHubButton';
import CodeBlock from '../ui/CodeBlock';

import styles from './index.module.scss';

import FeatureModifiers from '../code-samples/examples/modifiers-styles';
import FeatureInput from '../code-samples/examples/input';
import FeatureInput2 from '../code-samples/examples/input-from-to';
import FeatureLocalization from '../code-samples/examples/localization';
import FeatureRange from '../code-samples/examples/selected-range';

import imgLogo from '../images/logo-icon.png';

export default function HomePage() {
  return (
    <Page>

        <Wrap>

react-day-picker

            Date picker component for{' '}
            React

            Flexible, highly customizable, localizable, with ARIA support, no
            external dependencies,{' '}

              7.4kB gzipped

            .

            <LinkButton to="/docs">Read the docs</LinkButton>
            <LinkButton to="/examples">Browse the examples</LinkButton>

          <GitHubButton />
        </Wrap>

      <Wrap>

Style days with modifiers

              Define the disabled or selected days and change the aspect of each
              day cell with <Link to="/docs/matching-days">modifiers</Link>.

              <FeatureModifiers />

Works with input fields

              Display the date picker in an overlay using the{' '}
              <Link to="/docs/input">DayPickerInput</Link> component.

              <FeatureInput />

...or choose a range of days:

              <FeatureInput2 />

Localizable

              Use your own translation strings or import those from moment.js,
              if you use it.

              <FeatureLocalization />

Select range of days

              Specify which days should be selected in your component’s state (
              <Link to="/examples/selected-range">source</Link>
              ).

              <FeatureRange numberOfMonths={1} />

Get started

Install via npm or yarn

            <CodeBlock language="bash">
              {`$ npm install react-day-picker --save
# or with yarn:
$ yarn add react-day-picker`}
            </CodeBlock>

              {' '}

              {' '}

              {' '}

Import in your component

            <CodeBlock>{`import React from 'react';
import DayPicker from 'react-day-picker';
import 'react-day-picker/lib/style.css';

export default function Hello() {
  return <DayPicker />;
}
`}</CodeBlock>

      </Wrap>

    </Page>
  );
}
<|endoftext|>


<commit_before>const initialState = {
  drivers: []
}

export default (state = initialState, action) => {
  switch(action.type){
    case "SET_DRIVERS":
      return Object.assign({}, state, { drivers: action.drivers })
    default:
      return state
  }
}<commit_msg>add the reducer changes<commit_after>const initialState = {
  drivers: [],
  showModal: false,
  driver: {}
}

export default (state = initialState, action) => {
  switch(action.type){
    case "SET_DRIVERS":
      return Object.assign({}, state, { drivers: action.drivers })
    case "SHOW_DRIVER_MODAL":
      return Object.assign({}, state, { showModal: true, driver: action.driver})
    case "CLEAR_DRIVER_MODAL":
      return Object.assign({}, state, { showModal: false, driver: {}})
    default:
      return state
  }
}<|endoftext|>


<commit_before>var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener("offline", checkConnection, false);
        document.addEventListener("pause", onPause, false);
        document.addEventListener("resume", onResume, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
        screen.lockOrientation('portrait');
        localStorage.setItem("saldo_actual",0);
        localStorage.setItem("wisi",false);
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        getDeviceProperty();
        detectGEO();
    }
};
function detectGEO(){
  var options = {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 75000
  };
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
  } else {
      console.log('Geolocalización no soportada');
  }
}

// onSuccess Geolocation    //
function onSuccess(position) {
  localStorage.removeItem("position");
  lat1= position.coords.latitude;
  lng1= position.coords.longitude;
  var pos= {lat:lat1,lng:lng1};
  localStorage.setItem("position",JSON.stringify(pos));
  console.log('Coordenadas detectadas');
}
function onError(error) {
  console.log('Error detectando coordenadas');
}

function getDeviceProperty()
{
     var deviceOS  = device.platform  ;  //fetch the device operating system
     var deviceOSVersion = device.version ;  //fetch the device OS version
     localStorage.setItem("OS",deviceOS);
}

function onPause() {
    console.log("paused");
    localStorage.setItem("wisi","false");
    searchWISI();
}
function searchWISI(){
    window.setTimeout(function(){
        WifiWizard.listNetworks(listHandler, fail);
    },60000);
}

function onResume() {
  console.log("resume");
  localStorage.setItem("wisi","true");
}

function checkConnection() {
    var state=true;
    var networkState = navigator.connection.type;
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    if(states[networkState]=='No network connection'){
        //navigator.vibrate(1000);
        //alert('Internet no disponible!');
        state=false;
    }
    return state;
}

function fail(e){
    console.log("Detect active WIFI "+e);
}

function listHandler(a){
  //console.log("listar "+localStorage.wisi);
  if(localStorage.wisi=="false"){
    var exists=0;
    for(var i=0; i<a.length; i++){
        if((a[i].search("WISI TE CONECTA")>0||a[i].search("VALENCIA_V")>0)&&exists==0){
          if(localStorage.wisi=="false")
            //cordova.plugins.notification.local.schedule({ message:"Red WISI detectada" });
            myApp.alert("Red WISI detectada", "");
            localStorage.setItem("wisi","true");
            navigator.vibrate(1000);
            exists++;
          }
        }
    }
  }
  searchWISI();
}<commit_msg>config<commit_after>var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener("offline", checkConnection, false);
        document.addEventListener("pause", onPause, false);
        document.addEventListener("resume", onResume, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
        screen.lockOrientation('portrait');
        localStorage.setItem("saldo_actual",0);
        localStorage.setItem("wisi",false);
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        getDeviceProperty();
        detectGEO();
    }
};
function detectGEO(){
  var options = {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 75000
  };
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
  } else {
      console.log('Geolocalización no soportada');
  }
}

// onSuccess Geolocation    //
function onSuccess(position) {
  localStorage.removeItem("position");
  lat1= position.coords.latitude;
  lng1= position.coords.longitude;
  var pos= {lat:lat1,lng:lng1};
  localStorage.setItem("position",JSON.stringify(pos));
  console.log('Coordenadas detectadas');
}
function onError(error) {
  console.log('Error detectando coordenadas');
}

function getDeviceProperty()
{
     var deviceOS  = device.platform  ;  //fetch the device operating system
     var deviceOSVersion = device.version ;  //fetch the device OS version
     localStorage.setItem("OS",deviceOS);
}

function onPause() {
    localStorage.setItem("wisi","false");
    console.log("paused");
    searchWISI();
}
function searchWISI(){
    window.setTimeout(function(){
        WifiWizard.listNetworks(listHandler, fail);
    },60000);
}

function onResume() {
  localStorage.setItem("wisi","true");
  console.log("resume");
}

function checkConnection() {
    var state=true;
    var networkState = navigator.connection.type;
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    if(states[networkState]=='No network connection'){
        //navigator.vibrate(1000);
        //alert('Internet no disponible!');
        state=false;
    }
    return state;
}

function fail(e){
    console.log("Detect active WIFI "+e);
}

function listHandler(a){
  //console.log("listar "+localStorage.wisi);
  if(localStorage.wisi=="false"){
    var exists=0;
    for(var i=0; i<a.length; i++){
        if((a[i].search("WISI TE CONECTA")>0||a[i].search("VALENCIA_V")>0)&&exists==0){
          if(localStorage.wisi=="false")
            //cordova.plugins.notification.local.schedule({ message:"Red WISI detectada" });
            myApp.alert("Red WISI detectada", "");
            localStorage.setItem("wisi","true");
            navigator.vibrate(1000);
            exists++;
          }
        }
    }
  }
  searchWISI();
}<|endoftext|>