<commit_before>import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import {
  Text,
  View,
  ScrollView,
  Animated,
  Modal,
  TouchableWithoutFeedback,
  Dimensions,
  Platform,
  ViewPropTypes,
} from 'react-native';
import Ripple from 'react-native-material-ripple';
import { TextField } from 'react-native-material-textfield';

import DropdownItem from '../item';
import styles from './styles';

const minMargin = 8;
const maxMargin = 16;

export default class Dropdown extends PureComponent {
  static defaultProps = {
    disabled: false,

    rippleInsets: {
      top: 16,
      right: 0,
      bottom: -8,
      left: 0,
    },

    rippleOpacity: 0.54,
    shadeOpacity: 0.12,

    animationDuration: 225,
    fontSize: 16,

    textColor: 'rgba(0, 0, 0, .87)',
    itemColor: 'rgba(0, 0, 0, .54)',
    baseColor: 'rgba(0, 0, 0, .38)',

    itemCount: 4,
    itemPadding: 8,

    labelHeight: 32,
  };

  static propTypes = {
    disabled: PropTypes.bool,

    rippleInsets: PropTypes.shape({
      top: PropTypes.number,
      right: PropTypes.number,
      bottom: PropTypes.number,
      left: PropTypes.number,
    }),

    rippleOpacity: PropTypes.number,
    shadeOpacity: PropTypes.number,

    animationDuration: PropTypes.number,
    fontSize: PropTypes.number,

    value: PropTypes.string,
    data: PropTypes.arrayOf(PropTypes.shape({
      value: PropTypes.string,
      label: PropTypes.string,
    })),

    textColor: PropTypes.string,
    itemColor: PropTypes.string,
    selectedItemColor: PropTypes.string,
    baseColor: PropTypes.string,

    itemTextStyle: Text.propTypes.style,

    itemCount: PropTypes.number,
    itemPadding: PropTypes.number,

    labelHeight: PropTypes.number,

    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onChangeText: PropTypes.func,

    renderBase: PropTypes.func,
    renderAccessory: PropTypes.func,

    containerStyle: (ViewPropTypes || View.propTypes).style,

    dropdownPosition: PropTypes.number,
  };

  constructor(props) {
    super(props);

    this.onPress = this.onPress.bind(this);
    this.onClose = this.onClose.bind(this);
    this.onSelect = this.onSelect.bind(this);
    this.updateRippleRef = this.updateRef.bind(this, 'ripple');
    this.updateContainerRef = this.updateRef.bind(this, 'container');
    this.updateScrollRef = this.updateRef.bind(this, 'scroll');
    this.renderAccessory = this.renderAccessory.bind(this);

    this.blur = this.onClose;
    this.focus = this.onPress;

    let { value } = this.props;

    this.mounted = false;
    this.state = {
      opacity: new Animated.Value(0),
      selected: -1,
      modal: false,
      value,
    };
  }

  componentWillReceiveProps({ value }) {
    if (value !== this.props.value) {
      this.setState({ value });
    }
  }

  componentDidMount() {
    this.mounted = true;
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  onPress(event) {
    let {
      data = [],
      disabled,
      onFocus,
      rippleInsets,
      labelHeight,
      itemPadding,
      dropdownPosition,
      animationDuration,
    } = this.props;

    if (disabled) {
      return;
    }

    let itemCount = data.length;
    let visibleItemCount = this.visibleItemCount();
    let tailItemCount = this.tailItemCount();
    let timestamp = Date.now();

    if (null != event) {
      /* Adjust event location */
      event.nativeEvent.locationY -= rippleInsets.top;

      /* Start ripple directly from event */
      this.ripple.startRipple(event);
    }

    if (!itemCount) {
      return;
    }

    if ('function' === typeof onFocus) {
      onFocus();
    }

    let dimensions = Dimensions.get('window');

    this.container.measureInWindow((x, y, containerWidth, containerHeight) => {
      let { opacity } = this.state;

      let delay = Math.max(0, animationDuration - (Date.now() - timestamp));
      let selected = this.selectedIndex();
      let offset = 0;

      if (itemCount > visibleItemCount) {
        if (null == dropdownPosition) {
          switch (selected) {
            case -1:
              break;

            case 0:
            case 1:
              break;

            default:
              if (selected >= itemCount - tailItemCount) {
                offset = this.itemSize() * (itemCount - visibleItemCount);
              } else {
                offset = this.itemSize() * (selected - 1);
              }
          }
        } else {
          if (~selected) {
            if (dropdownPosition < 0) {
              offset = this.itemSize() * (selected - visibleItemCount - dropdownPosition);
            } else {
              offset = this.itemSize() * (selected - dropdownPosition);
            }
          }
        }
      }

      let left = x - maxMargin;
      let leftInset;

      if (left > minMargin) {
        leftInset = maxMargin;
      } else {
        left = minMargin;
        leftInset = minMargin;
      }

      let right = x + containerWidth + maxMargin;
      let rightInset;

      if (dimensions.width - right > minMargin) {
        rightInset = maxMargin;
      } else {
        right = dimensions.width - minMargin;
        rightInset = minMargin;
      }

      let top = y
        + Platform.select({ ios: 1, android: 2 })
        + labelHeight
        - itemPadding;

      this.setState({
        modal: true,
        width: right - left,
        top,
        left,
        leftInset,
        rightInset,
        selected,
      });

      setTimeout((() => {
        if (this.mounted) {
          this.scroll
            .scrollTo({ x: 0, y: offset, animated: false });

          Animated
            .timing(opacity, {
              duration: animationDuration,
              toValue: 1,
            })
            .start(() => {
              if (this.mounted && 'ios' === Platform.OS) {
                let { flashScrollIndicators } = this.scroll;

                if ('function' === typeof flashScrollIndicators) {
                  flashScrollIndicators.call(this.scroll);
                }
              }
            });
        }
      }), delay);
    });
  }

  onClose() {
    let { onBlur, animationDuration } = this.props;
    let { opacity } = this.state;

    Animated
      .timing(opacity, {
        duration: animationDuration,
        toValue: 0,
      })
      .start(() => {
        if ('function' === typeof onBlur) {
          onBlur();
        }

        if (this.mounted) {
          this.setState({ modal: false });
        }
      });
  }

  onSelect(index) {
    let { data, onChangeText, animationDuration } = this.props;
    let { value } = data[index];

    this.setState({ value });

    if ('function' === typeof onChangeText) {
      onChangeText(value, index, data);
    }

    setTimeout(this.onClose, animationDuration);
  }

  isFocused() {
    return this.state.modal;
  }

  selectedIndex() {
    let { data = [] } = this.props;

    return data
      .findIndex(({ value }) => value === this.state.value);
  }

  selectedItem() {
    let { data = [] } = this.props;

    return data
      .find(({ value }) => value === this.state.value);
  }

  itemSize() {
    let { fontSize, itemPadding } = this.props;

    return fontSize * 1.5 + itemPadding * 2;
  }

  visibleItemCount() {
    let { data = [], itemCount } = this.props;

    return Math.min(data.length, itemCount);
  }

  tailItemCount() {
    return Math.max(this.visibleItemCount() - 2, 0);
  }

  updateRef(name, ref) {
    this[name] = ref;
  }

  renderBase() {
    let { value } = this.state;
    let {
      containerStyle,
      rippleInsets,
      rippleOpacity,
      renderBase,
      renderAccessory = this.renderAccessory,
      ...props
    } = this.props;

    let { label = value } = this.selectedItem() || {};

    if ('function' === typeof renderBase) {
      return renderBase({ ...props, label, value, renderAccessory });
    }

    return (
      <TextField
        {...props}

        value={label}
        editable={false}
        onChangeText={undefined}
        renderAccessory={renderAccessory}
      />
    );
  }

  renderAccessory() {
    let { baseColor: backgroundColor } = this.props;
    let triangleStyle = { backgroundColor };

    return (
      <View style={styles.accessory}>
        <View style={styles.triangleContainer}>
          <View style={[styles.triangle, triangleStyle]} />
        </View>
      </View>
    );
  }

  renderItems() {
    let { selected, leftInset, rightInset } = this.state;

    let {
      data = [],
      textColor,
      itemColor,
      selectedItemColor = textColor,
      baseColor,
      fontSize,
      itemTextStyle,
      animationDuration,
      rippleOpacity,
      shadeOpacity,
    } = this.props;

    let props = {
      baseColor,
      fontSize,
      animationDuration,
      rippleOpacity,
      shadeOpacity,
      onPress: this.onSelect,
      style: {
        height: this.itemSize(),
        paddingLeft: leftInset,
        paddingRight: rightInset,
      },
    };

    return data
      .map(({ value, label = value }, index) => {
        let color = ~selected?
          index === selected?
            selectedItemColor:
            itemColor:
          selectedItemColor;

        let style = { color, fontSize };

        return (
          <DropdownItem index={index} key={index} {...props}>
            <Text style={[itemTextStyle, style]} numberOfLines={1}>
              {label}
            </Text>
          </DropdownItem>
        );
      });
  }

  render() {
    let {
      data = [],
      rippleOpacity,
      rippleInsets,
      containerStyle,
      baseColor,
      animationDuration,
      itemPadding,
      dropdownPosition,
    } = this.props;

    let { left, top, width, opacity, selected, modal } = this.state;

    let dimensions = Dimensions.get('window');

    let itemCount = data.length;
    let visibleItemCount = this.visibleItemCount();
    let tailItemCount = this.tailItemCount();
    let itemSize = this.itemSize();

    let overlayStyle = {
      width: dimensions.width,
      height: dimensions.height,
    };

    let height = 2 * itemPadding + itemSize * visibleItemCount;
    let translateY = -itemPadding;

    if (null == dropdownPosition) {
      switch (selected) {
        case -1:
          translateY -= 1 === itemCount? 0 : itemSize;
          break;

        case 0:
          break;

        default:
          if (selected >= itemCount - tailItemCount) {
            translateY -= itemSize * (visibleItemCount - (itemCount - selected));
          } else {
            translateY -= itemSize;
          }
      }
    } else {
      if (dropdownPosition < 0) {
        translateY -= itemSize * (visibleItemCount + dropdownPosition);
      } else {
        translateY -= itemSize * dropdownPosition;
      }
    }

    let pickerStyle = {
      width,
      height,
      top,
      left,
      opacity,
      transform: [{ translateY }],
    };

    let { bottom, ...insets } = rippleInsets;
    let rippleStyle = {
      ...insets,

      height: itemSize - bottom,
      position: 'absolute',
    };

    return (
      <View onLayout={() => undefined} ref={this.updateContainerRef} style={containerStyle}>
        <TouchableWithoutFeedback onPress={this.onPress}>
          <View pointerEvents='box-only'>
            {this.renderBase()}

            <Ripple
              style={rippleStyle}
              rippleColor={baseColor}
              rippleDuration={animationDuration * 2}
              rippleOpacity={rippleOpacity}
              rippleSequential={true}
              ref={this.updateRippleRef}
            />
          </View>
        </TouchableWithoutFeedback>

        <Modal visible={modal} transparent={true} onRequestClose={this.onClose}>
          <TouchableWithoutFeedback onPress={this.onClose}>
            <View style={overlayStyle}>
              <Animated.View style={[styles.picker, pickerStyle]}>
                <ScrollView
                  ref={this.updateScrollRef}
                  style={styles.scroll}
                  scrollEnabled={visibleItemCount < itemCount}
                  contentContainerStyle={styles.scrollContainer}
                >
                  {this.renderItems()}
                </ScrollView>
              </Animated.View>
            </View>
          </TouchableWithoutFeedback>
        </Modal>
      </View>
    );
  }
}
<commit_msg>Partial override for rippleInsets<commit_after>import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import {
  Text,
  View,
  ScrollView,
  Animated,
  Modal,
  TouchableWithoutFeedback,
  Dimensions,
  Platform,
  ViewPropTypes,
} from 'react-native';
import Ripple from 'react-native-material-ripple';
import { TextField } from 'react-native-material-textfield';

import DropdownItem from '../item';
import styles from './styles';

const minMargin = 8;
const maxMargin = 16;

export default class Dropdown extends PureComponent {
  static defaultProps = {
    disabled: false,

    rippleOpacity: 0.54,
    shadeOpacity: 0.12,

    animationDuration: 225,
    fontSize: 16,

    textColor: 'rgba(0, 0, 0, .87)',
    itemColor: 'rgba(0, 0, 0, .54)',
    baseColor: 'rgba(0, 0, 0, .38)',

    itemCount: 4,
    itemPadding: 8,

    labelHeight: 32,
  };

  static propTypes = {
    disabled: PropTypes.bool,

    rippleInsets: PropTypes.shape({
      top: PropTypes.number,
      right: PropTypes.number,
      bottom: PropTypes.number,
      left: PropTypes.number,
    }),

    rippleOpacity: PropTypes.number,
    shadeOpacity: PropTypes.number,

    animationDuration: PropTypes.number,
    fontSize: PropTypes.number,

    value: PropTypes.string,
    data: PropTypes.arrayOf(PropTypes.shape({
      value: PropTypes.string,
      label: PropTypes.string,
    })),

    textColor: PropTypes.string,
    itemColor: PropTypes.string,
    selectedItemColor: PropTypes.string,
    baseColor: PropTypes.string,

    itemTextStyle: Text.propTypes.style,

    itemCount: PropTypes.number,
    itemPadding: PropTypes.number,

    labelHeight: PropTypes.number,

    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onChangeText: PropTypes.func,

    renderBase: PropTypes.func,
    renderAccessory: PropTypes.func,

    containerStyle: (ViewPropTypes || View.propTypes).style,

    dropdownPosition: PropTypes.number,
  };

  constructor(props) {
    super(props);

    this.onPress = this.onPress.bind(this);
    this.onClose = this.onClose.bind(this);
    this.onSelect = this.onSelect.bind(this);
    this.updateRippleRef = this.updateRef.bind(this, 'ripple');
    this.updateContainerRef = this.updateRef.bind(this, 'container');
    this.updateScrollRef = this.updateRef.bind(this, 'scroll');
    this.renderAccessory = this.renderAccessory.bind(this);

    this.blur = this.onClose;
    this.focus = this.onPress;

    let { value } = this.props;

    this.mounted = false;
    this.state = {
      opacity: new Animated.Value(0),
      selected: -1,
      modal: false,
      value,
    };
  }

  componentWillReceiveProps({ value }) {
    if (value !== this.props.value) {
      this.setState({ value });
    }
  }

  componentDidMount() {
    this.mounted = true;
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  onPress(event) {
    let {
      data = [],
      disabled,
      onFocus,
      labelHeight,
      itemPadding,
      dropdownPosition,
      animationDuration,
    } = this.props;

    if (disabled) {
      return;
    }

    let itemCount = data.length;
    let visibleItemCount = this.visibleItemCount();
    let tailItemCount = this.tailItemCount();
    let timestamp = Date.now();

    if (null != event) {
      /* Adjust event location */
      event.nativeEvent.locationY -= this.rippleInsets().top;

      /* Start ripple directly from event */
      this.ripple.startRipple(event);
    }

    if (!itemCount) {
      return;
    }

    if ('function' === typeof onFocus) {
      onFocus();
    }

    let dimensions = Dimensions.get('window');

    this.container.measureInWindow((x, y, containerWidth, containerHeight) => {
      let { opacity } = this.state;

      let delay = Math.max(0, animationDuration - (Date.now() - timestamp));
      let selected = this.selectedIndex();
      let offset = 0;

      if (itemCount > visibleItemCount) {
        if (null == dropdownPosition) {
          switch (selected) {
            case -1:
              break;

            case 0:
            case 1:
              break;

            default:
              if (selected >= itemCount - tailItemCount) {
                offset = this.itemSize() * (itemCount - visibleItemCount);
              } else {
                offset = this.itemSize() * (selected - 1);
              }
          }
        } else {
          if (~selected) {
            if (dropdownPosition < 0) {
              offset = this.itemSize() * (selected - visibleItemCount - dropdownPosition);
            } else {
              offset = this.itemSize() * (selected - dropdownPosition);
            }
          }
        }
      }

      let left = x - maxMargin;
      let leftInset;

      if (left > minMargin) {
        leftInset = maxMargin;
      } else {
        left = minMargin;
        leftInset = minMargin;
      }

      let right = x + containerWidth + maxMargin;
      let rightInset;

      if (dimensions.width - right > minMargin) {
        rightInset = maxMargin;
      } else {
        right = dimensions.width - minMargin;
        rightInset = minMargin;
      }

      let top = y
        + Platform.select({ ios: 1, android: 2 })
        + labelHeight
        - itemPadding;

      this.setState({
        modal: true,
        width: right - left,
        top,
        left,
        leftInset,
        rightInset,
        selected,
      });

      setTimeout((() => {
        if (this.mounted) {
          this.scroll
            .scrollTo({ x: 0, y: offset, animated: false });

          Animated
            .timing(opacity, {
              duration: animationDuration,
              toValue: 1,
            })
            .start(() => {
              if (this.mounted && 'ios' === Platform.OS) {
                let { flashScrollIndicators } = this.scroll;

                if ('function' === typeof flashScrollIndicators) {
                  flashScrollIndicators.call(this.scroll);
                }
              }
            });
        }
      }), delay);
    });
  }

  onClose() {
    let { onBlur, animationDuration } = this.props;
    let { opacity } = this.state;

    Animated
      .timing(opacity, {
        duration: animationDuration,
        toValue: 0,
      })
      .start(() => {
        if ('function' === typeof onBlur) {
          onBlur();
        }

        if (this.mounted) {
          this.setState({ modal: false });
        }
      });
  }

  onSelect(index) {
    let { data, onChangeText, animationDuration } = this.props;
    let { value } = data[index];

    this.setState({ value });

    if ('function' === typeof onChangeText) {
      onChangeText(value, index, data);
    }

    setTimeout(this.onClose, animationDuration);
  }

  isFocused() {
    return this.state.modal;
  }

  selectedIndex() {
    let { data = [] } = this.props;

    return data
      .findIndex(({ value }) => value === this.state.value);
  }

  selectedItem() {
    let { data = [] } = this.props;

    return data
      .find(({ value }) => value === this.state.value);
  }

  itemSize() {
    let { fontSize, itemPadding } = this.props;

    return fontSize * 1.5 + itemPadding * 2;
  }

  visibleItemCount() {
    let { data = [], itemCount } = this.props;

    return Math.min(data.length, itemCount);
  }

  tailItemCount() {
    return Math.max(this.visibleItemCount() - 2, 0);
  }

  rippleInsets() {
    let {
      top = 16,
      right = 0,
      bottom = -8,
      left = 0,
    } = this.props.rippleInsets || {};

    return { top, right, bottom, left };
  }

  updateRef(name, ref) {
    this[name] = ref;
  }

  renderBase() {
    let { value } = this.state;
    let {
      containerStyle,
      rippleInsets,
      rippleOpacity,
      renderBase,
      renderAccessory = this.renderAccessory,
      ...props
    } = this.props;

    let { label = value } = this.selectedItem() || {};

    if ('function' === typeof renderBase) {
      return renderBase({ ...props, label, value, renderAccessory });
    }

    return (
      <TextField
        {...props}

        value={label}
        editable={false}
        onChangeText={undefined}
        renderAccessory={renderAccessory}
      />
    );
  }

  renderAccessory() {
    let { baseColor: backgroundColor } = this.props;
    let triangleStyle = { backgroundColor };

    return (
      <View style={styles.accessory}>
        <View style={styles.triangleContainer}>
          <View style={[styles.triangle, triangleStyle]} />
        </View>
      </View>
    );
  }

  renderItems() {
    let { selected, leftInset, rightInset } = this.state;

    let {
      data = [],
      textColor,
      itemColor,
      selectedItemColor = textColor,
      baseColor,
      fontSize,
      itemTextStyle,
      animationDuration,
      rippleOpacity,
      shadeOpacity,
    } = this.props;

    let props = {
      baseColor,
      fontSize,
      animationDuration,
      rippleOpacity,
      shadeOpacity,
      onPress: this.onSelect,
      style: {
        height: this.itemSize(),
        paddingLeft: leftInset,
        paddingRight: rightInset,
      },
    };

    return data
      .map(({ value, label = value }, index) => {
        let color = ~selected?
          index === selected?
            selectedItemColor:
            itemColor:
          selectedItemColor;

        let style = { color, fontSize };

        return (
          <DropdownItem index={index} key={index} {...props}>
            <Text style={[itemTextStyle, style]} numberOfLines={1}>
              {label}
            </Text>
          </DropdownItem>
        );
      });
  }

  render() {
    let {
      data = [],
      rippleOpacity,
      containerStyle,
      baseColor,
      animationDuration,
      itemPadding,
      dropdownPosition,
    } = this.props;

    let { left, top, width, opacity, selected, modal } = this.state;

    let dimensions = Dimensions.get('window');

    let itemCount = data.length;
    let visibleItemCount = this.visibleItemCount();
    let tailItemCount = this.tailItemCount();
    let itemSize = this.itemSize();

    let overlayStyle = {
      width: dimensions.width,
      height: dimensions.height,
    };

    let height = 2 * itemPadding + itemSize * visibleItemCount;
    let translateY = -itemPadding;

    if (null == dropdownPosition) {
      switch (selected) {
        case -1:
          translateY -= 1 === itemCount? 0 : itemSize;
          break;

        case 0:
          break;

        default:
          if (selected >= itemCount - tailItemCount) {
            translateY -= itemSize * (visibleItemCount - (itemCount - selected));
          } else {
            translateY -= itemSize;
          }
      }
    } else {
      if (dropdownPosition < 0) {
        translateY -= itemSize * (visibleItemCount + dropdownPosition);
      } else {
        translateY -= itemSize * dropdownPosition;
      }
    }

    let pickerStyle = {
      width,
      height,
      top,
      left,
      opacity,
      transform: [{ translateY }],
    };

    let { bottom, ...insets } = this.rippleInsets();
    let rippleStyle = {
      ...insets,

      height: itemSize - bottom,
      position: 'absolute',
    };

    return (
      <View onLayout={() => undefined} ref={this.updateContainerRef} style={containerStyle}>
        <TouchableWithoutFeedback onPress={this.onPress}>
          <View pointerEvents='box-only'>
            {this.renderBase()}

            <Ripple
              style={rippleStyle}
              rippleColor={baseColor}
              rippleDuration={animationDuration * 2}
              rippleOpacity={rippleOpacity}
              rippleSequential={true}
              ref={this.updateRippleRef}
            />
          </View>
        </TouchableWithoutFeedback>

        <Modal visible={modal} transparent={true} onRequestClose={this.onClose}>
          <TouchableWithoutFeedback onPress={this.onClose}>
            <View style={overlayStyle}>
              <Animated.View style={[styles.picker, pickerStyle]}>
                <ScrollView
                  ref={this.updateScrollRef}
                  style={styles.scroll}
                  scrollEnabled={visibleItemCount < itemCount}
                  contentContainerStyle={styles.scrollContainer}
                >
                  {this.renderItems()}
                </ScrollView>
              </Animated.View>
            </View>
          </TouchableWithoutFeedback>
        </Modal>
      </View>
    );
  }
}
<|endoftext|>


<commit_before>import Component from 'inferno-component'
import createElement from 'inferno-create-element'
import { View, throwError } from 'cerebral'

class BaseComponent extends Component {
  constructor(dependencies, mergeProps, props, controller, name) {
    super(props)
    if (!controller) {
      throwError(
        'Can not find controller, did you remember to use the Container component? Read more at: https://cerebraljs.com/docs/views/inferno.html'
      )
    }

    this._propsChange = false
    this.onUpdate = this.onUpdate.bind(this)
    this.view = new View({
      dependencies,
      mergeProps,
      props,
      controller,
      displayName: name,
      onUpdate: this.onUpdate,
    })
  }
  /*
    Register the component to the dependency store with its
    state tracker and tags state dependencies
  */
  componentWillMount() {
    this.view.mount()
  }
  /*
    We only allow forced render by change of props passed
    or Container tells it to render
  */
  shouldComponentUpdate() {
    if (this._propsChange) {
      this._propsChange = false
      return true
    }
    return false
  }
  /*
    If received props differ, we need to update any
    StateTrackers and tags, cause they might be using
    props to define a state dependency
  */
  componentWillReceiveProps(nextProps) {
    const hasUpdate = this.view.onPropsUpdate(this.props, nextProps)
    if (hasUpdate) {
      this.forceUpdate()
      this._propsChange = true
    }
  }
  /*
    Unregister with existing state dependencies
  */
  componentWillUnmount() {
    this.view.unMount()
  }
  onUpdate(stateChanges, force) {
    this.view.updateFromState(stateChanges, this.props, force)
    this.forceUpdate()
  }
}

export default function HOC(dependencies, mergeProps, Component) {
  class CerebralComponent extends BaseComponent {
    constructor(props, context) {
      super(
        dependencies,
        mergeProps,
        props,
        context.controller,
        Component.displayName || Component.name
      )
    }
    toJSON() {
      return this.view._displayName
    }
    render() {
      return createElement(Component, this.view.getProps(this.props))
    }
  }
  CerebralComponent.displayName = `CerebralWrapping_${Component.displayName ||
    Component.name}`

  return CerebralComponent
}
<commit_msg>fix(inferno): change render hook for SSR<commit_after>import Component from 'inferno-component'
import createElement from 'inferno-create-element'
import { View, throwError } from 'cerebral'

class BaseComponent extends Component {
  constructor(dependencies, mergeProps, props, controller, name) {
    super(props)
    if (!controller) {
      throwError(
        'Can not find controller, did you remember to use the Container component? Read more at: https://cerebraljs.com/docs/views/inferno.html'
      )
    }

    this._propsChange = false
    this.onUpdate = this.onUpdate.bind(this)
    this.view = new View({
      dependencies,
      mergeProps,
      props,
      controller,
      displayName: name,
      onUpdate: this.onUpdate,
    })
  }
  /*
    Register the component to the dependency store with its
    state tracker and tags state dependencies
  */
  componentDidMount() {
    this.view.mount()
  }
  /*
    We only allow forced render by change of props passed
    or Container tells it to render
  */
  shouldComponentUpdate() {
    if (this._propsChange) {
      this._propsChange = false
      return true
    }
    return false
  }
  /*
    If received props differ, we need to update any
    StateTrackers and tags, cause they might be using
    props to define a state dependency
  */
  componentWillReceiveProps(nextProps) {
    const hasUpdate = this.view.onPropsUpdate(this.props, nextProps)
    if (hasUpdate) {
      this.forceUpdate()
      this._propsChange = true
    }
  }
  /*
    Unregister with existing state dependencies
  */
  componentWillUnmount() {
    this.view.unMount()
  }
  onUpdate(stateChanges, force) {
    this.view.updateFromState(stateChanges, this.props, force)
    this.forceUpdate()
  }
}

export default function HOC(dependencies, mergeProps, Component) {
  class CerebralComponent extends BaseComponent {
    constructor(props, context) {
      super(
        dependencies,
        mergeProps,
        props,
        context.controller,
        Component.displayName || Component.name
      )
    }
    toJSON() {
      return this.view._displayName
    }
    render() {
      return createElement(Component, this.view.getProps(this.props))
    }
  }
  CerebralComponent.displayName = `CerebralWrapping_${Component.displayName ||
    Component.name}`

  return CerebralComponent
}
<|endoftext|>


<commit_before>(function (appConfig) {
  'use strict';

  // *** main dependencies *** //
  const express = require('express');
  const path = require('path');
  const cookieParser = require('cookie-parser');
  const bodyParser = require('body-parser');
  const session = require('express-session');
  const flash = require('connect-flash');
  const morgan = require('morgan');
  const nunjucks = require('nunjucks');
  const passport = require('passport');
  // const LocalStrategy = require('passport-local').Strategy;

  // *** view folders *** //
  const viewFolders = [
    path.join(__dirname, '..', 'views')
  ];

  // *** load environment variables *** //
  require('dotenv').config();

  appConfig.init = (app, express) => {
    // *** view engine *** //
    nunjucks.configure(viewFolders, {
      express: app,
      autoescape: true
    });
    app.set('view engine', 'html');

    // *** app middleware *** //
    if (process.env.NODE_ENV !== 'test') {
      app.use(morgan('dev'));
    }

    // *** cross domain requests ***
    const allowCrossDomain = (req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      next();
    };

    app.use(allowCrossDomain);
    app.use(cookieParser());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(session({
      secret: process.env.SECRET_KEY,
      resave: false,
      saveUninitialized: true
    }));
    app.use(flash());
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(express.static(path.join(__dirname, '..', '..', 'client')));
  };
})(module.exports);
<commit_msg>deploy attempt<commit_after>(function (appConfig) {
  'use strict';

  // *** main dependencies *** //
  // const express = require('express');
  const path = require('path');
  const cookieParser = require('cookie-parser');
  const bodyParser = require('body-parser');
  // const session = require('express-session');
  const flash = require('connect-flash');
  const morgan = require('morgan');
  const nunjucks = require('nunjucks');
  const passport = require('passport');
  // const LocalStrategy = require('passport-local').Strategy;

  // *** view folders *** //
  const viewFolders = [
    path.join(__dirname, '..', 'views')
  ];

  // *** load environment variables *** //
  require('dotenv').config();

  appConfig.init = (app, express) => {
    // *** view engine *** //
    nunjucks.configure(viewFolders, {
      express: app,
      autoescape: true
    });
    app.set('view engine', 'html');

    // *** app middleware *** //
    if (process.env.NODE_ENV !== 'test') {
      app.use(morgan('dev'));
    }

    // *** cross domain requests ***
    const allowCrossDomain = (req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      next();
    };

    app.use(allowCrossDomain);
    app.use(cookieParser());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    // app.use(session({
    //   secret: process.env.SECRET_KEY,
    //   resave: false,
    //   saveUninitialized: true
    // }));
    app.use(flash());
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(express.static(path.join(__dirname, '..', '..', 'client')));
  };
})(module.exports);
<|endoftext|>


<commit_before>$(function() {

    $("input,textarea").jqBootstrapValidation({
        preventSubmit: true,
        submitError: function($form, event, errors) {
            // additional error messages or events
        },
        submitSuccess: function($form, event) {
            event.preventDefault(); // prevent default submit behaviour
            // get values from FORM
            var name = $("input#name").val();
            var email = $("input#email").val();
            var phone = $("input#phone").val();
            var message = $("textarea#message").val();
            var firstName = name; // For Success/Failure Message
            // Check for white space in name for Success/Fail message
            if (firstName.indexOf(' ') >= 0) {
                firstName = name.split(' ').slice(0, -1).join(' ');
            }
            $.ajax({
                url: "././mail/contact_me.php",
                type: "POST",
                data: {
                    name: name,
                    phone: phone,
                    email: email,
                    message: message
                },
                cache: false,
                success: function() {
                    // Success message
                    $('#success').html("
");
                    $('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
                        .append("</button>");
                    $('#success > .alert-success')
                        .append("Your message has been sent. ");
                    $('#success > .alert-success')
                        .append('
');

                    //clear all fields
                    $('#contactForm').trigger("reset");
                },
                error: function() {
                    // Fail message
                    $('#success').html("
");
                    $('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
                        .append("</button>");
                    $('#success > .alert-danger').append("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!");
                    $('#success > .alert-danger').append('
');
                    //clear all fields
                    $('#contactForm').trigger("reset");
                },
            })
        },
        filter: function() {
            return $(this).is(":visible");
        },
    });

    $("a[data-toggle=\"tab\"]").click(function(e) {
        e.preventDefault();
        $(this).tab("show");
    });
});

/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
    $('#success').html('');
});
<commit_msg>Delete contact_me.js<commit_after><|endoftext|>


<commit_before>// NOTE: This code is called using the jtalkc bash script - do not use directly.
// The arguments variable is a series of .st filenames and category names.
// If it is a .st file we import it, if it is a category name we export it
// as aCategoryName.js.
var sys = require('sys'), fs = require('fs');

// Only care about our arguments, strip away node and all.js
var arguments = process.argv.splice(2);

// If it ends with .st, import it, otherwise export category as .js
arguments.forEach(function(val, index, array) {
  if (/\.st/.test(val)) {
    sys.puts("Reading file " + val);
    code = fs.readFileSync(val, "utf8");
    smalltalk.Importer._new()._import_(code._stream());
  } else {
    sys.puts("Exporting category " + val);
    fs.writeFileSync(val + ".js", smalltalk.Exporter._new()._exportCategory_(val));
  }
});
<commit_msg>Oops, forgot to also commit change to nodecompile.js<commit_after>// NOTE: This code is called using the jtalkc bash script - do not use directly.
// The arguments variable is a series of .st filenames and category names.
// If it is a .st file we import it, if it is a category name we export it
// as aCategoryName.js.
var sys = require('sys'), fs = require('fs');

// Only care about our arguments, strip away node, all.js and debug flag.
var arguments = process.argv.splice(3);

// First argument is debugMode: "true" or "false"
if (process.argv[2] == "true") {
  smalltalk.debugMode = true;
} else {
  smalltalk.debugMode = false;
}
console.log("Compiling in debugMode: " + smalltalk.debugMode);

// If it ends with .st, import it, otherwise export category as .js
arguments.forEach(function(val, index, array) {
  if (/\.st/.test(val)) {
    sys.puts("Reading file " + val);
    code = fs.readFileSync(val, "utf8");
    smalltalk.Importer._new()._import_(code._stream());
  } else {
    sys.puts("Exporting category " + val);
    fs.writeFileSync(val + ".js", smalltalk.Exporter._new()._exportCategory_(val));
  }
});
<|endoftext|>


<commit_before>const express = require('express')
const qs = require('querystring')
const urlParser = require('url')
const companion = require('../companion')
const helmet = require('helmet')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const redis = require('../server/redis')
const merge = require('lodash.merge')
// @ts-ignore
const promBundle = require('express-prom-bundle')
const session = require('express-session')
const addRequestId = require('express-request-id')()
const helper = require('./helper')
// @ts-ignore
const { version } = require('../../package.json')

const app = express()

// for server metrics tracking.
const metricsMiddleware = promBundle({ includeMethod: true })
const promClient = metricsMiddleware.promClient
const collectDefaultMetrics = promClient.collectDefaultMetrics
const promInterval = collectDefaultMetrics({ register: promClient.register, timeout: 5000 })

// Add version as a prometheus gauge
const versionGauge = new promClient.Gauge({ name: 'companion_version', help: 'npm version as an integer' })
// @ts-ignore
const numberVersion = version.replace(/\D/g, '') * 1
versionGauge.set(numberVersion)

if (app.get('env') !== 'test') {
  clearInterval(promInterval)
}

app.use(addRequestId)
// log server requests.
app.use(morgan('combined'))
morgan.token('url', (req, res) => {
  const query = Object.assign({}, req.query)
  let hasQuery = false;
  ['access_token', 'uppyAuthToken'].forEach((key) => {
    if (req.query && req.query[key]) {
      // replace logged access token with xxxx character
      query[key] = 'x'.repeat(req.query[key].length)
      hasQuery = true
    }
  })

  return hasQuery ? `${req.path}?${qs.stringify(query)}` : req.originalUrl || req.url
})

morgan.token('referrer', (req, res) => {
  const ref = req.headers.referer || req.headers.referrer
  if (typeof ref === 'string') {
    // @todo drop the use of url.parse
    // when support for node 6 is dropped
    // eslint-disable-next-line
    const parsed = urlParser.URL ? new urlParser.URL(ref) : urlParser.parse(ref)
    const query = qs.parse(parsed.search.replace('?', ''));
    ['uppyAuthToken', 'access_token'].forEach(key => {
      if (query[key]) {
        query[key] = 'x'.repeat(query[key].length)
      }
    })

    const hasQuery = parsed.search
    const newURL = `${parsed.href.split('?')[0]}?${qs.stringify(query)}`
    return hasQuery ? newURL : parsed.href
  }
})

// make app metrics available at '/metrics'.
app.use(metricsMiddleware)

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// Use helmet to secure Express headers
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.noSniff())
app.use(helmet.ieNoOpen())
app.disable('x-powered-by')

const companionOptions = helper.getCompanionOptions()
const sessionOptions = {
  secret: companionOptions.secret,
  resave: true,
  saveUninitialized: true
}

if (companionOptions.redisUrl) {
  const RedisStore = require('connect-redis')(session)
  const redisClient = redis.client(
    merge({ url: companionOptions.redisUrl }, companionOptions.redisOptions)
  )
  sessionOptions.store = new RedisStore({ client: redisClient })
}

if (process.env.COMPANION_COOKIE_DOMAIN) {
  sessionOptions.cookie = {
    domain: process.env.COMPANION_COOKIE_DOMAIN,
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}

app.use(session(sessionOptions))

app.use((req, res, next) => {
  const protocol = process.env.COMPANION_PROTOCOL || 'http'

  // if endpoint urls are specified, then we only allow those endpoints
  // otherwise, we allow any client url to access companion.
  // here we also enforce that only the protocol allowed by companion is used.
  if (process.env.COMPANION_CLIENT_ORIGINS) {
    const whitelist = process.env.COMPANION_CLIENT_ORIGINS
      .split(',')
      .map((url) => helper.hasProtocol(url) ? url : `${protocol}://${url}`)

    // @ts-ignore
    if (req.headers.origin && whitelist.indexOf(req.headers.origin) > -1) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
      // only allow credentials when origin is whitelisted
      res.setHeader('Access-Control-Allow-Credentials', 'true')
    }
  } else {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
  }

  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
  )
  res.setHeader(
    'Access-Control-Allow-Headers',
    'Authorization, Origin, Content-Type, Accept'
  )
  next()
})

// Routes
app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/plain')
  res.send(helper.buildHelpfulStartupMessage(companionOptions))
})

// initialize companion
helper.validateConfig(companionOptions)
if (process.env.COMPANION_PATH) {
  app.use(process.env.COMPANION_PATH, companion.app(companionOptions))
} else {
  app.use(companion.app(companionOptions))
}

// WARNING: This route is added in order to validate your app with OneDrive.
// Only set COMPANION_ONEDRIVE_DOMAIN_VALIDATION if you are sure that you are setting the
// correct value for COMPANION_ONEDRIVE_KEY (i.e application ID). If there's a slightest possiblilty
// that you might have mixed the values for COMPANION_ONEDRIVE_KEY and COMPANION_ONEDRIVE_SECRET,
// please do not set a value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
if (process.env.COMPANION_ONEDRIVE_DOMAIN_VALIDATION === 'true' && process.env.COMPANION_ONEDRIVE_KEY) {
  app.get('/.well-known/microsoft-identity-association.json', (req, res) => {
    const content = JSON.stringify({
      associatedApplications: [
        { applicationId: process.env.COMPANION_ONEDRIVE_KEY }
      ]
    })
    res.header('Content-Length', `${content.length}`)
    // use writeHead to prevent 'charset' from being appended
    // https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-configure-publisher-domain#to-select-a-verified-domain
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(content)
  })
}

app.use((req, res, next) => {
  return res.status(404).json({ message: 'Not Found' })
})

if (app.get('env') === 'production') {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: 'Something went wrong', requestId: req.id })
  })
} else {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: err.message, error: err, requestId: req.id })
  })
}

module.exports = { app, companionOptions }
<commit_msg>companion: use OneDrive validation content verbatim<commit_after>const express = require('express')
const qs = require('querystring')
const urlParser = require('url')
const companion = require('../companion')
const helmet = require('helmet')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const redis = require('../server/redis')
const merge = require('lodash.merge')
// @ts-ignore
const promBundle = require('express-prom-bundle')
const session = require('express-session')
const addRequestId = require('express-request-id')()
const helper = require('./helper')
// @ts-ignore
const { version } = require('../../package.json')

const app = express()

// for server metrics tracking.
const metricsMiddleware = promBundle({ includeMethod: true })
const promClient = metricsMiddleware.promClient
const collectDefaultMetrics = promClient.collectDefaultMetrics
const promInterval = collectDefaultMetrics({ register: promClient.register, timeout: 5000 })

// Add version as a prometheus gauge
const versionGauge = new promClient.Gauge({ name: 'companion_version', help: 'npm version as an integer' })
// @ts-ignore
const numberVersion = version.replace(/\D/g, '') * 1
versionGauge.set(numberVersion)

if (app.get('env') !== 'test') {
  clearInterval(promInterval)
}

app.use(addRequestId)
// log server requests.
app.use(morgan('combined'))
morgan.token('url', (req, res) => {
  const query = Object.assign({}, req.query)
  let hasQuery = false;
  ['access_token', 'uppyAuthToken'].forEach((key) => {
    if (req.query && req.query[key]) {
      // replace logged access token with xxxx character
      query[key] = 'x'.repeat(req.query[key].length)
      hasQuery = true
    }
  })

  return hasQuery ? `${req.path}?${qs.stringify(query)}` : req.originalUrl || req.url
})

morgan.token('referrer', (req, res) => {
  const ref = req.headers.referer || req.headers.referrer
  if (typeof ref === 'string') {
    // @todo drop the use of url.parse
    // when support for node 6 is dropped
    // eslint-disable-next-line
    const parsed = urlParser.URL ? new urlParser.URL(ref) : urlParser.parse(ref)
    const query = qs.parse(parsed.search.replace('?', ''));
    ['uppyAuthToken', 'access_token'].forEach(key => {
      if (query[key]) {
        query[key] = 'x'.repeat(query[key].length)
      }
    })

    const hasQuery = parsed.search
    const newURL = `${parsed.href.split('?')[0]}?${qs.stringify(query)}`
    return hasQuery ? newURL : parsed.href
  }
})

// make app metrics available at '/metrics'.
app.use(metricsMiddleware)

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// Use helmet to secure Express headers
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.noSniff())
app.use(helmet.ieNoOpen())
app.disable('x-powered-by')

const companionOptions = helper.getCompanionOptions()
const sessionOptions = {
  secret: companionOptions.secret,
  resave: true,
  saveUninitialized: true
}

if (companionOptions.redisUrl) {
  const RedisStore = require('connect-redis')(session)
  const redisClient = redis.client(
    merge({ url: companionOptions.redisUrl }, companionOptions.redisOptions)
  )
  sessionOptions.store = new RedisStore({ client: redisClient })
}

if (process.env.COMPANION_COOKIE_DOMAIN) {
  sessionOptions.cookie = {
    domain: process.env.COMPANION_COOKIE_DOMAIN,
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}

app.use(session(sessionOptions))

app.use((req, res, next) => {
  const protocol = process.env.COMPANION_PROTOCOL || 'http'

  // if endpoint urls are specified, then we only allow those endpoints
  // otherwise, we allow any client url to access companion.
  // here we also enforce that only the protocol allowed by companion is used.
  if (process.env.COMPANION_CLIENT_ORIGINS) {
    const whitelist = process.env.COMPANION_CLIENT_ORIGINS
      .split(',')
      .map((url) => helper.hasProtocol(url) ? url : `${protocol}://${url}`)

    // @ts-ignore
    if (req.headers.origin && whitelist.indexOf(req.headers.origin) > -1) {
      res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
      // only allow credentials when origin is whitelisted
      res.setHeader('Access-Control-Allow-Credentials', 'true')
    }
  } else {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
  }

  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
  )
  res.setHeader(
    'Access-Control-Allow-Headers',
    'Authorization, Origin, Content-Type, Accept'
  )
  next()
})

// Routes
app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/plain')
  res.send(helper.buildHelpfulStartupMessage(companionOptions))
})

// initialize companion
helper.validateConfig(companionOptions)
if (process.env.COMPANION_PATH) {
  app.use(process.env.COMPANION_PATH, companion.app(companionOptions))
} else {
  app.use(companion.app(companionOptions))
}

// WARNING: This route is added in order to validate your app with OneDrive.
// Only set COMPANION_ONEDRIVE_DOMAIN_VALIDATION if you are sure that you are setting the
// correct value for COMPANION_ONEDRIVE_KEY (i.e application ID). If there's a slightest possiblilty
// that you might have mixed the values for COMPANION_ONEDRIVE_KEY and COMPANION_ONEDRIVE_SECRET,
// please do not set a value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
if (process.env.COMPANION_ONEDRIVE_DOMAIN_VALIDATION === 'true' && process.env.COMPANION_ONEDRIVE_KEY) {
  app.get('/.well-known/microsoft-identity-association.json', (req, res) => {
    const content =
`{
  "associatedApplications": [
    {
      "applicationId": "${process.env.COMPANION_ONEDRIVE_KEY}"
    }
  ]
}`
    res.header('Content-Length', `${Buffer.byteLength(content, 'utf8')}`)
    // use writeHead to prevent 'charset' from being appended
    // https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-configure-publisher-domain#to-select-a-verified-domain
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(content)
  })
}

app.use((req, res, next) => {
  return res.status(404).json({ message: 'Not Found' })
})

if (app.get('env') === 'production') {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: 'Something went wrong', requestId: req.id })
  })
} else {
  // @ts-ignore
  app.use((err, req, res, next) => {
    console.error('\x1b[31m', req.id, err, '\x1b[0m')
    res.status(err.status || 500).json({ message: err.message, error: err, requestId: req.id })
  })
}

module.exports = { app, companionOptions }
<|endoftext|>


<commit_before>var cc = require("coupon-code");
var parseDiscount = require("./parse-discount");

function Coupon(param){
    if(!param) param = {};
    if(!this instanceof Coupon) return Coupon.create({});
    var voided = false;

    var count = param["count"] || 0;
    Object.defineProperty(this, "count", {
        get: function(){
            return count;
        }
    });

    var id = param["id"] || cc.generate({parts: 1, partLen: 32});
    Object.defineProperty(this, "id", {
        enumerable: true,
        get: function(){
            return id;
        }
    });

    this.name = cc.generate({parts: 3, partLen: 2});
    this.user = undefined;
    this.expirationDate = undefined;
    this.service = undefined;
    this.discount = 0;
    this.countMax = Infinity;

    if(param instanceof Object){
        for(var i in param){
            if(i in handlers) {
                this[handlers[i]](param[i]);
            }else{
                this[i] = param[i];
            }
        }
    }

    this.isPublic = function () {
        return !this.user;
    };

    this.isUnlimited = function () {
        var no_exp = this.expirationDate == null;
        var no_lim = this.countMax == Infinity;
        return no_exp && no_lim;
    };

    this.isExpired = function () {
        if (voided) return true;
        if (!this.expirationDate) return false;
        return this.expirationDate.getTime() < Date.now();
    };

    this.isVoided = function () {
        return voided;
    };

    this.invalidate = function () {
        voided = true;
        return this;
    };

    this.apply = function(){
        if(this.countMax == Infinity){
            count ++;
        }else{
            if(count < this.countMax){
                count ++;
                if(count <= this.countMax) this.invalidate();
            }else{
                throw new Error("cannot apply voided coupon");
            }
        }

        return this;
    }
}

/**
 * Creates an instance of Coupon
 * @param param
 * @returns {Coupon}
 */
Coupon.create = function (param){
    var coupon = new Coupon();
    if(param && typeof param == "string"){
        // coupon = new Coupon();
        coupon.name = param;
    }else if(param instanceof Object){
        coupon = new Coupon(param);
        // for(var i in param){
        //     coupon[i] = param[i];
        // }
    }
    return coupon;
};

Coupon.prototype.limit = function(param){
    if(!param){
        this.countMax = 0;
    }else if(typeof param == "number"){
        this.countMax = Math.floor(param);
    }

    return this;
};

Coupon.prototype.person = function(name){
    if(!name) {
        this.user = undefined;
    }else if(typeof name == "string"){
        this.user = name;
    }

    return this;
};

Coupon.prototype.only = function(sevice){
    if(!sevice) {
        this.service = undefined;
    }else if(typeof sevice == "string"){
        this.service = sevice;
    }else if(sevice instanceof Array){
        this.service = sevice;
    }

    return this;
};

Coupon.prototype.expire = function(date){
    if(!date) {
        this.expirationDate = undefined;
    }else if(date instanceof Date){
        this.expirationDate = date;
    }else if(typeof date == "number"){
        this.expire(new Date(date));
    }
    return this;
};

Coupon.prototype.give = function(discount){
    if(!discount) {
        this.discount = 0;
    }else if(typeof discount == "number"){
        this.discount = Math.abs(discount) / 100;
    }else if(typeof discount == "string"){
        var discount = parseDiscount(discount);
        if(discount == "free") this.discount = "free";
        else this.give(discount);
    }
    return this;
};

Coupon.prototype.json = function(){
    var out = {};
    out.id = this.id;
    if(this.name) out.name = this.name;
    if(this.user) out.user = this.user;
    if(this.count) out.count = this.count;
    if(this.countMax != Infinity) out.countMax= this.countMax;
    if(this.service) out.service = this.service;
    if(this.expirationDate) out.expirationDate = this.expirationDate;
    out.discount = this.discount || 0;
    return out;
};

Coupon.prototype.profit = function(param){
    if(typeof  param == "number"){
        return calculateDiscount(param, this.discount);
    }else{
        return 0;
    }
};

var handlers = {
    "discount": "give"
};

function calculateDiscount(price, discount){
    if(discount == "free") return price;
    else return discount * price;
}

module.exports = Coupon;<commit_msg>Coupon#give duplicate variable fix<commit_after>var cc = require("coupon-code");
var parseDiscount = require("./parse-discount");

function Coupon(param){
    if(!param) param = {};
    if(!this instanceof Coupon) return Coupon.create({});
    var voided = false;

    var count = param["count"] || 0;
    Object.defineProperty(this, "count", {
        get: function(){
            return count;
        }
    });

    var id = param["id"] || cc.generate({parts: 1, partLen: 32});
    Object.defineProperty(this, "id", {
        enumerable: true,
        get: function(){
            return id;
        }
    });

    this.name = cc.generate({parts: 3, partLen: 2});
    this.user = undefined;
    this.expirationDate = undefined;
    this.service = undefined;
    this.discount = 0;
    this.countMax = Infinity;

    if(param instanceof Object){
        for(var i in param){
            if(i in handlers) {
                this[handlers[i]](param[i]);
            }else{
                this[i] = param[i];
            }
        }
    }

    this.isPublic = function () {
        return !this.user;
    };

    this.isUnlimited = function () {
        var no_exp = this.expirationDate == null;
        var no_lim = this.countMax == Infinity;
        return no_exp && no_lim;
    };

    this.isExpired = function () {
        if (voided) return true;
        if (!this.expirationDate) return false;
        return this.expirationDate.getTime() < Date.now();
    };

    this.isVoided = function () {
        return voided;
    };

    this.invalidate = function () {
        voided = true;
        return this;
    };

    this.apply = function(){
        if(this.countMax == Infinity){
            count ++;
        }else{
            if(count < this.countMax){
                count ++;
                if(count <= this.countMax) this.invalidate();
            }else{
                throw new Error("cannot apply voided coupon");
            }
        }

        return this;
    }
}

/**
 * Creates an instance of Coupon
 * @param param
 * @returns {Coupon}
 */
Coupon.create = function (param){
    var coupon = new Coupon();
    if(param && typeof param == "string"){
        // coupon = new Coupon();
        coupon.name = param;
    }else if(param instanceof Object){
        coupon = new Coupon(param);
        // for(var i in param){
        //     coupon[i] = param[i];
        // }
    }
    return coupon;
};

Coupon.prototype.limit = function(param){
    if(!param){
        this.countMax = 0;
    }else if(typeof param == "number"){
        this.countMax = Math.floor(param);
    }

    return this;
};

Coupon.prototype.person = function(name){
    if(!name) {
        this.user = undefined;
    }else if(typeof name == "string"){
        this.user = name;
    }

    return this;
};

Coupon.prototype.only = function(sevice){
    if(!sevice) {
        this.service = undefined;
    }else if(typeof sevice == "string"){
        this.service = sevice;
    }else if(sevice instanceof Array){
        this.service = sevice;
    }

    return this;
};

Coupon.prototype.expire = function(date){
    if(!date) {
        this.expirationDate = undefined;
    }else if(date instanceof Date){
        this.expirationDate = date;
    }else if(typeof date == "number"){
        this.expire(new Date(date));
    }
    return this;
};

Coupon.prototype.give = function(discount){
    if(!discount) {
        this.discount = 0;
    }else if(typeof discount == "number"){
        this.discount = Math.abs(discount) / 100;
    }else if(typeof discount == "string"){
        discount = parseDiscount(discount);
        if(discount == "free") this.discount = "free";
        else this.give(discount);
    }
    return this;
};

Coupon.prototype.json = function(){
    var out = {};
    out.id = this.id;
    if(this.name) out.name = this.name;
    if(this.user) out.user = this.user;
    if(this.count) out.count = this.count;
    if(this.countMax != Infinity) out.countMax= this.countMax;
    if(this.service) out.service = this.service;
    if(this.expirationDate) out.expirationDate = this.expirationDate;
    out.discount = this.discount || 0;
    return out;
};

Coupon.prototype.profit = function(param){
    if(typeof  param == "number"){
        return calculateDiscount(param, this.discount);
    }else{
        return 0;
    }
};

var handlers = {
    "discount": "give"
};

function calculateDiscount(price, discount){
    if(discount == "free") return price;
    else return discount * price;
}

module.exports = Coupon;<|endoftext|>


<commit_before>'use strict';

/**
 * A field for managing structures of other fields
 *
 * @memberof HashBrown.Client.Entity.View.Field
 */
class StructEditor extends HashBrown.Entity.View.Field.FieldBase {
    /**
     * Constructor
     */
    constructor(params) {
        super(params);

        this.editorTemplate = require('template/field/editor/structEditor');
        this.configTemplate = require('template/field/config/structEditor');

        this.state.isCollapsible = true;
        this.state.isCollapsed = this.state.name !== 'config';
    }

    /**
     * Fetches view data
     */
    async fetch() {
        this.state.fields = {};

        if(this.state.name === 'config') {
            // Build label options
            this.state.labelOptions = {};

            for(let key in this.model.config.struct || {}) {
                let label = this.model.config.struct[key] ? this.model.config.struct[key].label || key : key;

                this.state.labelOptions[label] = key;
            }
        }

        if(!this.state.value || typeof this.state.value !== 'object' || Array.isArray(this.state.value)) {
            this.state.value = {};
        }

        if(!this.model.config) { this.model.config = {}; }
        if(!this.model.config.struct) { this.model.config.struct = {}; }

        for(let key in this.model.config.struct) {
            let definition = this.model.config.struct[key];

            // When in the config state, only the label and key is needed
            if(this.state.name === 'config') {
                this.state.fields[key] = definition.label;

            // When in the editor state, show all fields as normal
            } else {
                let view = await HashBrown.Entity.View.Field.FieldBase.createFromFieldDefinition(
                    definition,
                    this.state.value ? this.state.value[key] : null
                );

                view.on('change', (newValue) => {
                    if(!this.state.value) { this.state.value = {}; }
                    this.state.value[key] = newValue;

                    this.trigger('change', this.state.value);
                });

                this.state.fields[key] = view;
            }
        }
    }

    /**
     * Gets the value label
     *
     * @return {String} Value label
     */
    getValueLabel() {
        if(!this.model.config || !this.state.value || !this.state.value[this.model.config.label]) { return super.getValueLabel(); }

        let label = this.state.value[this.model.config.label];

        if(typeof label === 'object') {
            if(Array.isArray(label)) {
                return '(array)';
            }

            if(label[HashBrown.Context.language]) {
                return label[HashBrown.Context.language];
            }

            if(Object.keys(label).length > 0) {
                return Object.keys(label)[0] + ': ' + Object.values(label)[0];
            }

            return '(object)';
        }

        return label;
    }

    /**
     * Event: Click edit field
     *
     * @param {String} key
     */
    onClickEditField(key) {
        if(!this.model.config.struct) { this.model.config.struct = {}; }
        if(!this.model.config.struct[key]) { this.model.config.struct[key] = { schemaId: 'string' }; }

        let modal = new HashBrown.Entity.View.Modal.EditField({
            model: {
                key: key,
                definition: this.model.config.struct[key]
            }
        });

        modal.on('changekey', (newKey) => {
            this.onChangeConfigStructKey(key, newKey);

            key = newKey;
        });

        modal.on('change', (newValue) => {
            this.onChangeConfigStructValue(key, newValue);
        });
    }

    /**
     * Event: Change config key
     */
    onChangeConfigStructKey(oldKey, newKey) {
        let keys = Object.keys(this.model.config.struct);

        let newFields = {};

        for(let key of keys) {
            let value = this.model.config.struct[key];

            if(key === oldKey) { key = newKey; }

            newFields[key] = value;
        }

        this.model.config.struct = newFields;

        this.onChange();
        this.update();
    }

    /**
     * Event: Change config struct
     */
    onChangeConfigStructValue(key, newValue) {
        if(!this.model.config.struct) { this.model.config.struct = {}; }
        if(!this.model.config.struct[key]) { this.model.config.struct[key] = {}; }

        this.model.config.struct[key] = newValue;

        this.onChange();
        this.update();
    }

    /**
     * Event: Change field sorting
     */
    onChangeFieldSorting(fields) {
        let newFields = {};

        for(let key in fields) {
            let value = this.model.config.struct[key];

            newFields[key] = value || { label: fields[key] };
        }

        let isNewField = Object.keys(fields).length > Object.keys(this.model.config.struct).length;

        this.model.config.struct = newFields;
        this.onChange();

        if(isNewField) {
            this.onClickEditField(Object.keys(fields).pop());
        }
    }
}

module.exports = StructEditor;
<commit_msg>Fixed multilingual issue in StructEditor<commit_after>'use strict';

/**
 * A field for managing structures of other fields
 *
 * @memberof HashBrown.Client.Entity.View.Field
 */
class StructEditor extends HashBrown.Entity.View.Field.FieldBase {
    /**
     * Constructor
     */
    constructor(params) {
        super(params);

        this.editorTemplate = require('template/field/editor/structEditor');
        this.configTemplate = require('template/field/config/structEditor');

        this.state.isCollapsible = true;
        this.state.isCollapsed = this.state.name !== 'config';
    }

    /**
     * Fetches view data
     */
    async fetch() {
        this.state.fields = {};

        if(this.state.name === 'config') {
            // Build label options
            this.state.labelOptions = {};

            for(let key in this.model.config.struct || {}) {
                let label = this.model.config.struct[key] ? this.model.config.struct[key].label || key : key;

                this.state.labelOptions[label] = key;
            }
        }

        if(!this.state.value || typeof this.state.value !== 'object' || Array.isArray(this.state.value)) {
            this.state.value = {};
        }

        if(!this.model.config) { this.model.config = {}; }
        if(!this.model.config.struct) { this.model.config.struct = {}; }

        for(let key in this.model.config.struct) {
            let definition = this.model.config.struct[key];

            // When in the config state, only the label and key is needed
            if(this.state.name === 'config') {
                this.state.fields[key] = definition.label;

            // When in the editor state, show all fields as normal
            } else {
                let view = await HashBrown.Entity.View.Field.FieldBase.createFromFieldDefinition(
                    definition,
                    this.state.value ? this.state.value[key] : null
                );

                view.on('change', (newValue) => {
                    if(!this.state.value) { this.state.value = {}; }
                    this.state.value[key] = newValue;

                    this.onChange(this.state.value);
                });

                this.state.fields[key] = view;
            }
        }
    }

    /**
     * Gets the value label
     *
     * @return {String} Value label
     */
    getValueLabel() {
        if(!this.model.config || !this.state.value || !this.state.value[this.model.config.label]) { return super.getValueLabel(); }

        let label = this.state.value[this.model.config.label];

        if(typeof label === 'object') {
            if(Array.isArray(label)) {
                return '(array)';
            }

            if(label[HashBrown.Context.language]) {
                return label[HashBrown.Context.language];
            }

            if(Object.keys(label).length > 0) {
                return Object.keys(label)[0] + ': ' + Object.values(label)[0];
            }

            return '(object)';
        }

        return label;
    }

    /**
     * Event: Click edit field
     *
     * @param {String} key
     */
    onClickEditField(key) {
        if(!this.model.config.struct) { this.model.config.struct = {}; }
        if(!this.model.config.struct[key]) { this.model.config.struct[key] = { schemaId: 'string' }; }

        let modal = new HashBrown.Entity.View.Modal.EditField({
            model: {
                key: key,
                definition: this.model.config.struct[key]
            }
        });

        modal.on('changekey', (newKey) => {
            this.onChangeConfigStructKey(key, newKey);

            key = newKey;
        });

        modal.on('change', (newValue) => {
            this.onChangeConfigStructValue(key, newValue);
        });
    }

    /**
     * Event: Change config key
     */
    onChangeConfigStructKey(oldKey, newKey) {
        let keys = Object.keys(this.model.config.struct);

        let newFields = {};

        for(let key of keys) {
            let value = this.model.config.struct[key];

            if(key === oldKey) { key = newKey; }

            newFields[key] = value;
        }

        this.model.config.struct = newFields;

        this.onChange();
        this.update();
    }

    /**
     * Event: Change config struct
     */
    onChangeConfigStructValue(key, newValue) {
        if(!this.model.config.struct) { this.model.config.struct = {}; }
        if(!this.model.config.struct[key]) { this.model.config.struct[key] = {}; }

        this.model.config.struct[key] = newValue;

        this.onChange();
        this.update();
    }

    /**
     * Event: Change field sorting
     */
    onChangeFieldSorting(fields) {
        let newFields = {};

        for(let key in fields) {
            let value = this.model.config.struct[key];

            newFields[key] = value || { label: fields[key] };
        }

        let isNewField = Object.keys(fields).length > Object.keys(this.model.config.struct).length;

        this.model.config.struct = newFields;
        this.onChange();

        if(isNewField) {
            this.onClickEditField(Object.keys(fields).pop());
        }
    }
}

module.exports = StructEditor;
<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
import history from '../history'
import BooksList from './BooksList';
import SmartSearch from 'smart-search';
import classNames from 'classnames';
import {List, Map} from 'immutable';
import * as Utils from '../utils';

export default React.createClass({

	getDefaultProps() {
		books: List([])
	},

	getInitialState() {
		return {
			mode: undefined,
			searchTerm: '',
			search: false
		}
	},

	handleSearch(e) {
		let searchTerm = e.target.value;

		this.setState({
			search: searchTerm.length > 0,
			searchTerm: searchTerm.length > 0 ? searchTerm : ''
		});
	},

	filterBooks(allBooks, mode, language, genre) {
		let books = null;

		if (null != genre && genre != 'all') {
			allBooks = allBooks.filter(book => book.genres.has(parseInt(genre)));
		}

		if (mode == 'unread') {
			books = allBooks.filter(b => b.is_read == 0);
		} else if (mode == 'read') {
			books = allBooks.filter(b => b.is_read == 1);
		} else {
			books = allBooks;
		}

		if (null != language && language != 'all') {
			books = books.filter(b => b.language_id == language);
		}

		if (this.state.search) {
			books = Utils.filter(books, this.state.searchTerm, ['title']);
		}

		return books;
	},

	getMode(mode) {
		return mode != 'read' && mode != 'unread' ? 'all' : mode;
	},

	render() {
		const {language, genre} = this.props.params;

		let mode = this.getMode(this.props.params.mode);
		let books = this.filterBooks(this.props.books, mode, language, genre);

		return (

{this.getTitle()}

					By language:

Finnish |

Swedish |

English |

All

					{this.getSelectedGenre(genre)}

					<input type="text" value={this.state.searchTerm} onChange={this.handleSearch}/>

				<BooksList {...this.props} books={books} />

		);
	},

	getSelectedGenre(genre) {
		const {genres} = this.props;

		if (null != genres && null != genre && genre != 'all') {
			genre = parseInt(genre);
			if (genres.has(genre)) {
				return 'By genre: ' + genres.get(genre).name;
			}
		}
	},

	getTitle() {
		let title = null;

		switch(this.props.params.mode) {
			case 'read': {
				title = 'Read books';
				break;
			}
			case 'unread': {
				title = 'Unread books';
				break;
			}
			default: {
				title = 'Books';
			}
		}

		return title;
	},

	getUrl(mode, language, genre) {
		return '/#/books/' + mode + (null != language ? '/' + language : '/all') + (null != genre ? '/' + genre : '	');
	}
});
<commit_msg>using <Link> for some reason, the language urla param had spaces appended, fixed it by calling trim()<commit_after>import React from 'react';
import _ from 'lodash';
import history from '../history'
import BooksList from './BooksList';
import SmartSearch from 'smart-search';
import classNames from 'classnames';
import {List, Map} from 'immutable';
import * as Utils from '../utils';
import {Link} from 'react-router';

export default React.createClass({

	getDefaultProps() {
		books: List([])
	},

	getInitialState() {
		return {
			mode: undefined,
			searchTerm: '',
			search: false
		}
	},

	handleSearch(e) {
		let searchTerm = e.target.value;

		this.setState({
			search: searchTerm.length > 0,
			searchTerm: searchTerm.length > 0 ? searchTerm : ''
		});
	},

	filterBooks(allBooks, mode, language, genre) {
		let books = null;
		if (null != language) {
			language = language.trim();
		}

		if (null != genre && genre != 'all') {
			allBooks = allBooks.filter(book => book.genres.has(parseInt(genre)));
		}

		if (mode == 'unread') {
			books = allBooks.filter(b => b.is_read == 0);
		} else if (mode == 'read') {
			books = allBooks.filter(b => b.is_read == 1);
		} else {
			books = allBooks;
		}

		if (null != language && language != 'all') {
			books = books.filter(b => b.language_id == language);
		}

		if (this.state.search) {
			books = Utils.filter(books, this.state.searchTerm, ['title']);
		}

		return books;
	},

	getMode(mode) {
		return mode != 'read' && mode != 'unread' ? 'all' : mode;
	},

	render() {
		const {language, genre} = this.props.params;
		let mode = this.getMode(this.props.params.mode);
		let books = this.filterBooks(this.props.books, mode, language, genre);

		return (

{this.getTitle()}

					By language:

<Link to={this.getUrl(mode, 'fi', genre)}>Finnish</Link> |

<Link to={this.getUrl(mode, 'se', genre)}>Swedish</Link> |

<Link to={this.getUrl(mode, 'en', genre)}>English</Link> |

<Link to={this.getUrl(mode, null, genre)}>All</Link>

					{this.getSelectedGenre(genre)}

					<input type="text" value={this.state.searchTerm} onChange={this.handleSearch}/>

				<BooksList {...this.props} books={books} />

		);
	},

	getSelectedGenre(genre) {
		const {genres} = this.props;

		if (null != genres && null != genre && genre != 'all') {
			genre = parseInt(genre);
			if (genres.has(genre)) {
				return 'By genre: ' + genres.get(genre).name;
			}
		}
	},

	getTitle() {
		let title = null;

		switch(this.props.params.mode) {
			case 'read': {
				title = 'Read books';
				break;
			}
			case 'unread': {
				title = 'Unread books';
				break;
			}
			default: {
				title = 'Books';
			}
		}

		return title;
	},

	getUrl(mode, language, genre) {
		return '/books/' + mode + (null != language ? '/' + language : '/all') + (null != genre ? '/' + genre : '	');
	}
});
<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { ButtonCore } from "../Button";
import messages from "../utils/messages.js";

/**
 * Master Dropdown Component
 * Only used for dropdown component
 * Supports required, disabled, custom width, custom error messaging, onclick and onchange functions
 */
class Dropdown extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      "active": false,
      "output": this._getOutput(),
      "value": this._getValue(),
      "valid": true,
      "errorMessage": "This field is required",
      "onChange": "",
      "focus": false,
      "zIndex": false
    };
    let isFocus = false;
  }

  _getOutput = () => {
    for(var i = 0; i < this.props.children.length; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.children
      }
    }
    if(this.props.defaultText){
      return this.props.defaultText;
    } else {
      return messages.selectOne;
    }
  }

  _getValue = () => {
    for(var i = 0; i < this.props.children.length; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.value
      }
    }
  }

  /**
   * Listeners added which call functions for window click outside of dropdown and blur outside of browser
   */
  componentDidMount() {
    window.addEventListener("blur", this._onWindowBlur);
  }

  /**
   * Listeners added which call functions for window click outside of dropdown and blur outside of browser
   */
  componentWillUnmount() {
    window.removeEventListener("blur", this._onWindowBlur);
  }

  /**
   * closes dropdown click outside of browser window
   */
  _onWindowBlur = () => {
    if (this.state.active === true) {
      this.setState({
        "active": false,
        "zIndex": false
      });
    }
  };

  /**
   *  _clickHandler is used when the dropdown option is selected.
   *
   */
  _clickHandler = (i) => {
    if (!this.props.disabled) {
      if (
        typeof this.props.onBeforeChange === "undefined" ||
        this.props.onBeforeChange(this.state.active)
      ) {
        const output = this.props.children[i].props.children;
        const inputValue = this.props.children[i].props.value;
        this.setState({
          "index": i,
          "output": output,
          "active": !this.state.active,
          "value": inputValue,
          "valid": true,
          "zIndex": false
        }, function(){
          this._validationHandler(this.props.errorCallback);
          if (this.props.onChange) {
            this._customOnchangeEvent(i, this.props.children);
          }
          if (this.props.onClick) {
            this._customOnclickEvent(i, this.props.children);
          }
        });

      }
    }
  };

  _customOnChangeEvent = event => {
    /* Pass the event object, and a data object to the click handler.
    The data object contains a boolean for whether the dropdown was
    changed or not, plus all the props passed to the object.  */
    this.props.onClick(event, {
      "active": this.state.valid,
      "props": this.props
    });
  };

  _customOnClickEvent = event => {
    /* Pass the event object, and a data object to the click handler.
      The data object contains a boolean for whether the dropdown was
      clicked or not, plus all the props passed to the object.  */

    this.props.onClick(event, {
      "active": this.state.valid,
      "props": this.props
    });
  };

  _toggle = (focus, event) => {
    /* Toggles the dropdown from active to inactive state, sets valid to true and zIndex to true.
      Active is used to show/hide options, valid is used to show/hide error messaging related to validation and zIndex sets a class on the component to ensure it has the proper index on the DOM
     */
    let canProceed = true;
    if(focus === "click" && this.isFocus) {
      canProceed = false;
      this.isFocus = false;
    } else if (focus === true) {
      this.isFocus = true;
    }
    if (!this.props.disabled && canProceed) {
      console.log(focus + ", " + this.state.active);
      const action = (focus && !this.state.active) ? true : false;
      console.log(action);
      if(action){
        this.setState({
          "focus": true,
          "active": true,
          "valid": true,
          "zIndex": true
        });
      } else {
        /* When the user exits dropdown the state is change for focus and validation method is called
         */
        this.setState({ "focus": false, "active": false, "zIndex": false });
        this._validationHandler(this.props.errorCallback);
      }

      if (this.props.onClick) {
        this._customOnclickEvent(event);
      }
    }
  };

  _validationHandler = callback => {
    /* Checks that required has been set to true and determines if errorCallback message was passed in a custom error message.
      Also sets state of valid depending on user action
      */
    let validationObject = {
      "valid":
        this.props.required && this.state.value !== "" ||
          !this.props.required,
      "message": this.state.errorMessage
    };
    if(callback){
      validationObject = callback(event, this.state.value);
    }
    this.setState({
      "valid": validationObject.valid,
      "errorMessage": validationObject.message
    });
  };

  _keyDown = event => {
    const indexValid = (typeof this.state.index === "number");
    let newIndex;
    if (event.key === "ArrowDown") {
      event.preventDefault();
      newIndex = (indexValid) ? this.state.index + 1 : 0;
      if(newIndex < this.props.children.length) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "ArrowUp") {
      event.preventDefault();
      newIndex = this.state.index - 1;
      if(newIndex >= 0) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "Enter") {
      event.preventDefault();
      this._clickHandler(this.state.index);
    }
  }

  render() {
    const {
      children,
      className,
      required,
      customLabel,
      buttonWidth,
      disabled
    } = this.props;
    const active = this.state.active;
    const error = !this.state.valid && !disabled ? true : false;
    let zIndex = this.state.zIndex ? true : false;
    const classes = cx({
      "active": active,
      "container": true,
      "zIndex": zIndex
    });

    const buttonClasses = cx({
      "ra_dropdown__dropdown-button": true,
      "ra_dropdown__error": error,
      "ra_dropdown__disabledClass": disabled
    });

    // Builds the option list from the children passed in
    // firstChild, lastChild and selected each have unique styling and those classes are added here
    const bound_children = children.map((child, i) => {
      let childClasses = cx({
        "ra_dropdown__selected": i === this.state.index,
        "ra_dropdown__firstChild": i === 0,
        "ra_dropdown__lastChild": i === children.length - 1
      });
      let kid =

 { // onMouseDown fires before onBlur. If changed to onClick it will fire after onBlur and not work.
            this._clickHandler(i, e);
          }}
        >
          {child}

      ;
      return kid;
    });

    const dropdownButtonClasses = cx(buttonClasses);

    return (

 {
          this.wrapperRef = node;
        }}
        className={className}
        styleName={classes}
        onFocus={(e) => {
          this._toggle(true, e);
        }}
        onBlur={(e) => {
          this._toggle(false, e);
        }}
        onKeyDown={e => {
          this._keyDown(e);
        }}
      >
        {customLabel &&

            {customLabel}
            {required &&
              *
            }

        }

 { this._toggle("click", e); }} styleName={cx("content")}>

            <ButtonCore
              styleName={cx("buttonClass")}
              className={dropdownButtonClasses}
            >
              {this.state.output}
            </ButtonCore>

          {this.state.active &&
            {bound_children}
          }
          <input type="hidden" value={this.state.value} />

        {error &&

            {this.state.errorMessage}

        }

    );
  }
}

Dropdown.propTypes = {
  /**
   * Text for dropdown label
   * @examples 'Some Label'
   */
  "customLabel": PropTypes.string,

  /* Boolean value taht tells the dropdown whether to
    be open or not.*/
  "active": PropTypes.bool,

  /* Boolean value that tells the dropdown whether the value is valid and controls error message is returns false.*/
  "valid": PropTypes.bool,

  /**
   * If included, dropdown is disabled
   * @examples <Dropdown disabled />, <Dropdown disabled={true} />
   */
  "disabled": PropTypes.bool,

  /**
   * Allows user to pass a callback for click events.
   */
  "onClick": PropTypes.func,

  /**
   * Allows user to pass a function to be executed when the dropdown state is changed.
   */
  "onChange": PropTypes.func,

  /**
   * If included, dropdown will return and error onBlur or onChange if not checked.
   */
  "required": PropTypes.bool,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  "errorCallback": PropTypes.func,

  /* . */
  "clickEvent": PropTypes.func,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  "errorCallback": PropTypes.func,

  /* The children elements to be wrapped by the dropdown menu. */
  "children": PropTypes.node.isRequired,

  /* Pass CSS styles to className to set them on the dropdown component. */
  "className": PropTypes.string,

  /* Default text to show in collapsed dropdown on initial render */
  "defaultText": PropTypes.string,

  /* Allows user to ask for user feedback before changing the selected value of the Dropdown. */
  "onBeforeChange": PropTypes.func,

  /* Allows user to set custom width of dropdown */
  "buttonWidth": PropTypes.string
};

Dropdown.defaultProps = {
  "className": "",
  "buttonWidth": "160",
  "required": false,
  "disabled": false
};

export default Dropdown;
<commit_msg>remove console.logs<commit_after>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { ButtonCore } from "../Button";
import messages from "../utils/messages.js";

/**
 * Master Dropdown Component
 * Only used for dropdown component
 * Supports required, disabled, custom width, custom error messaging, onclick and onchange functions
 */
class Dropdown extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      "active": false,
      "output": this._getOutput(),
      "value": this._getValue(),
      "valid": true,
      "errorMessage": "This field is required",
      "onChange": "",
      "focus": false,
      "zIndex": false
    };
    let isFocus = false;
  }

  _getOutput = () => {
    for(var i = 0; i < this.props.children.length; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.children
      }
    }
    if(this.props.defaultText){
      return this.props.defaultText;
    } else {
      return messages.selectOne;
    }
  }

  _getValue = () => {
    for(var i = 0; i < this.props.children.length; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.value
      }
    }
  }

  /**
   * Listeners added which call functions for window click outside of dropdown and blur outside of browser
   */
  componentDidMount() {
    window.addEventListener("blur", this._onWindowBlur);
  }

  /**
   * Listeners added which call functions for window click outside of dropdown and blur outside of browser
   */
  componentWillUnmount() {
    window.removeEventListener("blur", this._onWindowBlur);
  }

  /**
   * closes dropdown click outside of browser window
   */
  _onWindowBlur = () => {
    if (this.state.active === true) {
      this.setState({
        "active": false,
        "zIndex": false
      });
    }
  };

  /**
   *  _clickHandler is used when the dropdown option is selected.
   *
   */
  _clickHandler = (i) => {
    if (!this.props.disabled) {
      if (
        typeof this.props.onBeforeChange === "undefined" ||
        this.props.onBeforeChange(this.state.active)
      ) {
        const output = this.props.children[i].props.children;
        const inputValue = this.props.children[i].props.value;
        this.setState({
          "index": i,
          "output": output,
          "active": !this.state.active,
          "value": inputValue,
          "valid": true,
          "zIndex": false
        }, function(){
          this._validationHandler(this.props.errorCallback);
          if (this.props.onChange) {
            this._customOnchangeEvent(i, this.props.children);
          }
          if (this.props.onClick) {
            this._customOnclickEvent(i, this.props.children);
          }
        });

      }
    }
  };

  _customOnChangeEvent = event => {
    /* Pass the event object, and a data object to the click handler.
    The data object contains a boolean for whether the dropdown was
    changed or not, plus all the props passed to the object.  */
    this.props.onClick(event, {
      "active": this.state.valid,
      "props": this.props
    });
  };

  _customOnClickEvent = event => {
    /* Pass the event object, and a data object to the click handler.
      The data object contains a boolean for whether the dropdown was
      clicked or not, plus all the props passed to the object.  */

    this.props.onClick(event, {
      "active": this.state.valid,
      "props": this.props
    });
  };

  _toggle = (focus, event) => {
    /* Toggles the dropdown from active to inactive state, sets valid to true and zIndex to true.
      Active is used to show/hide options, valid is used to show/hide error messaging related to validation and zIndex sets a class on the component to ensure it has the proper index on the DOM
     */
    let canProceed = true;
    if(focus === "click" && this.isFocus) {
      canProceed = false;
      this.isFocus = false;
    } else if (focus === true) {
      this.isFocus = true;
    }
    if (!this.props.disabled && canProceed) {
      const action = (focus && !this.state.active) ? true : false;
      if(action){
        this.setState({
          "focus": true,
          "active": true,
          "valid": true,
          "zIndex": true
        });
      } else {
        /* When the user exits dropdown the state is change for focus and validation method is called
         */
        this.setState({ "focus": false, "active": false, "zIndex": false });
        this._validationHandler(this.props.errorCallback);
      }

      if (this.props.onClick) {
        this._customOnclickEvent(event);
      }
    }
  };

  _validationHandler = callback => {
    /* Checks that required has been set to true and determines if errorCallback message was passed in a custom error message.
      Also sets state of valid depending on user action
      */
    let validationObject = {
      "valid":
        this.props.required && this.state.value !== "" ||
          !this.props.required,
      "message": this.state.errorMessage
    };
    if(callback){
      validationObject = callback(event, this.state.value);
    }
    this.setState({
      "valid": validationObject.valid,
      "errorMessage": validationObject.message
    });
  };

  _keyDown = event => {
    const indexValid = (typeof this.state.index === "number");
    let newIndex;
    if (event.key === "ArrowDown") {
      event.preventDefault();
      newIndex = (indexValid) ? this.state.index + 1 : 0;
      if(newIndex < this.props.children.length) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "ArrowUp") {
      event.preventDefault();
      newIndex = this.state.index - 1;
      if(newIndex >= 0) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "Enter") {
      event.preventDefault();
      this._clickHandler(this.state.index);
    }
  }

  render() {
    const {
      children,
      className,
      required,
      customLabel,
      buttonWidth,
      disabled
    } = this.props;
    const active = this.state.active;
    const error = !this.state.valid && !disabled ? true : false;
    let zIndex = this.state.zIndex ? true : false;
    const classes = cx({
      "active": active,
      "container": true,
      "zIndex": zIndex
    });

    const buttonClasses = cx({
      "ra_dropdown__dropdown-button": true,
      "ra_dropdown__error": error,
      "ra_dropdown__disabledClass": disabled
    });

    // Builds the option list from the children passed in
    // firstChild, lastChild and selected each have unique styling and those classes are added here
    const bound_children = children.map((child, i) => {
      let childClasses = cx({
        "ra_dropdown__selected": i === this.state.index,
        "ra_dropdown__firstChild": i === 0,
        "ra_dropdown__lastChild": i === children.length - 1
      });
      let kid =

 { // onMouseDown fires before onBlur. If changed to onClick it will fire after onBlur and not work.
            this._clickHandler(i, e);
          }}
        >
          {child}

      ;
      return kid;
    });

    const dropdownButtonClasses = cx(buttonClasses);

    return (

 {
          this.wrapperRef = node;
        }}
        className={className}
        styleName={classes}
        onFocus={(e) => {
          this._toggle(true, e);
        }}
        onBlur={(e) => {
          this._toggle(false, e);
        }}
        onKeyDown={e => {
          this._keyDown(e);
        }}
      >
        {customLabel &&

            {customLabel}
            {required &&
              *
            }

        }

 { this._toggle("click", e); }} styleName={cx("content")}>

            <ButtonCore
              styleName={cx("buttonClass")}
              className={dropdownButtonClasses}
            >
              {this.state.output}
            </ButtonCore>

          {this.state.active &&
            {bound_children}
          }
          <input type="hidden" value={this.state.value} />

        {error &&

            {this.state.errorMessage}

        }

    );
  }
}

Dropdown.propTypes = {
  /**
   * Text for dropdown label
   * @examples 'Some Label'
   */
  "customLabel": PropTypes.string,

  /* Boolean value taht tells the dropdown whether to
    be open or not.*/
  "active": PropTypes.bool,

  /* Boolean value that tells the dropdown whether the value is valid and controls error message is returns false.*/
  "valid": PropTypes.bool,

  /**
   * If included, dropdown is disabled
   * @examples <Dropdown disabled />, <Dropdown disabled={true} />
   */
  "disabled": PropTypes.bool,

  /**
   * Allows user to pass a callback for click events.
   */
  "onClick": PropTypes.func,

  /**
   * Allows user to pass a function to be executed when the dropdown state is changed.
   */
  "onChange": PropTypes.func,

  /**
   * If included, dropdown will return and error onBlur or onChange if not checked.
   */
  "required": PropTypes.bool,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  "errorCallback": PropTypes.func,

  /* . */
  "clickEvent": PropTypes.func,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  "errorCallback": PropTypes.func,

  /* The children elements to be wrapped by the dropdown menu. */
  "children": PropTypes.node.isRequired,

  /* Pass CSS styles to className to set them on the dropdown component. */
  "className": PropTypes.string,

  /* Default text to show in collapsed dropdown on initial render */
  "defaultText": PropTypes.string,

  /* Allows user to ask for user feedback before changing the selected value of the Dropdown. */
  "onBeforeChange": PropTypes.func,

  /* Allows user to set custom width of dropdown */
  "buttonWidth": PropTypes.string
};

Dropdown.defaultProps = {
  "className": "",
  "buttonWidth": "160",
  "required": false,
  "disabled": false
};

export default Dropdown;
<|endoftext|>


<commit_before>var q = require('q');
var _ = require('lodash');

/**
 * Filters can modify or remove any user in the array. They should handle empty arrays.
 * Each filter should be exported as a function that accepts an array of users and returns a promise that resolves
 * with the new modified array of users. This could be the array that was passed in or a new array.
 * Most filters will be synchronous, but they should return a promise to support possible asychronous handling in the future.
 *
 * @param users
 * @returns {*}
 */
module.exports = function(users) {
  _.each(users, function(user) {
    user.sendEmail = false;
    user.sentText = false;
  });

  return q(users);
};
<commit_msg>fixes sendText var in filter<commit_after>var q = require('q');
var _ = require('lodash');

/**
 * Filters can modify or remove any user in the array. They should handle empty arrays.
 * Each filter should be exported as a function that accepts an array of users and returns a promise that resolves
 * with the new modified array of users. This could be the array that was passed in or a new array.
 * Most filters will be synchronous, but they should return a promise to support possible asychronous handling in the future.
 *
 * @param users
 * @returns {*}
 */
module.exports = function(users) {
  _.each(users, function(user) {
    user.sendEmail = false;
    user.sendText = false;
  });

  return q(users);
};
<|endoftext|>