<commit_before>/**
 * @file List component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

import ListItem from '../_ListItem';
import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import SelectMode from '../_statics/SelectMode';
import LIST_SEPARATOR from '../_statics/ListSeparator';

export default class List extends Component {

    static SelectMode = SelectMode;
    static LIST_SEPARATOR = LIST_SEPARATOR;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: this.initValue(props)
        };

        this.initValue = ::this.initValue;
        this.isItemChecked = ::this.isItemChecked;
        this.listItemTouchTapHandler = ::this.listItemTouchTapHandler;
        this.listItemSelectHandler = ::this.listItemSelectHandler;
        this.listItemDeselectHandler = ::this.listItemDeselectHandler;
        this.wheelHandler = ::this.wheelHandler;

    }

    initValue(props) {

        if (!props) {
            return;
        }

        const {value, selectMode} = props;

        if (!selectMode) {
            return;
        }

        if (value) {
            return value;
        }

        switch (selectMode) {
            case List.SelectMode.MULTI_SELECT:
                return [];
            case List.SelectMode.SINGLE_SELECT:
                return null;
            default:
                return value;
        }

    }

    isItemChecked(item) {

        const {selectMode, valueField, displayField} = this.props,
            {value} = this.state;

        if (!value) {
            return false;
        }

        if (selectMode === List.SelectMode.MULTI_SELECT) {
            return _.isArray(value) && value.filter(valueItem => {
                    return Util.isValueEqual(valueItem, item, valueField, displayField);
                }).length > 0;
        } else if (selectMode === List.SelectMode.SINGLE_SELECT) {
            return Util.isValueEqual(value, item, valueField, displayField);
        }

    }

    listItemTouchTapHandler(value, index) {

        const {selectMode} = this.props;

        if (selectMode !== List.SelectMode.NORMAL) {
            return;
        }

        this.setState({
            value
        }, () => {
            const {onItemTouchTap, onChange} = this.props;
            onItemTouchTap && onItemTouchTap(value, index);
            onChange && onChange(value, index);
        });

    }

    listItemSelectHandler(item, index) {

        const {selectMode} = this.props;

        if (selectMode === List.SelectMode.NORMAL) {
            return;
        }

        let {value} = this.state;

        if (selectMode === List.SelectMode.MULTI_SELECT) {

            if (!value || !_.isArray(value)) {
                value = [];
            }

            value.push(item);

        } else if (selectMode === List.SelectMode.SINGLE_SELECT) {
            value = item;
        }

        this.setState({
            value
        }, () => {
            const {onItemSelect, onChange} = this.props;
            onItemSelect && onItemSelect(item, index);
            onChange && onChange(value, index);
        });

    }

    listItemDeselectHandler(item, index) {

        const {selectMode} = this.props;

        if (selectMode !== List.SelectMode.MULTI_SELECT) {
            return;
        }

        const {valueField, displayField} = this.props;
        let {value} = this.state;

        if (!value || !_.isArray(value)) {
            value = [];
        } else {
            value = value.filter(valueItem => {
                return Util.getValueByValueField(valueItem, valueField, displayField)
                    != Util.getValueByValueField(item, valueField, displayField);
            });
        }

        this.setState({
            value
        }, () => {
            const {onItemDeselect, onChange} = this.props;
            onItemDeselect && onItemDeselect(item, index);
            onChange && onChange(value, index);
        });

    }

    wheelHandler(e) {
        const {shouldPreventContainerScroll, onWheel} = this.props;
        shouldPreventContainerScroll && Event.preventContainerScroll(e);
        onWheel && onWheel(e);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: this.initValue(nextProps)
            });
        }
    }

    render() {

        const {

                children, className, style, theme, data, itemHeight,

                selectTheme, selectMode, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                idField, valueField, displayField, descriptionField, disabled, isLoading, renderer

            } = this.props,
            listClassName = (className ? ' ' + className : '');

        return (

                {
                    _.isArray(data) && data.length > 0 ?
                        (
                            data.map((item, index) => {

                                if (item === LIST_SEPARATOR) {
                                    return

;
                                }

                                return typeof item === 'object' ?
                                    (
                                        <ListItem key={item[idField] || index}
                                                  {...item}
                                                  index={index}
                                                  style={{height: itemHeight}}
                                                  theme={item.theme || theme}
                                                  selectTheme={item.selectTheme || selectTheme}
                                                  radioUncheckedIconCls={item.radioUncheckedIconCls || radioUncheckedIconCls}
                                                  radioCheckedIconCls={item.radioCheckedIconCls || radioCheckedIconCls}
                                                  checkboxUncheckedIconCls={item.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkboxCheckedIconCls={item.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  checkboxIndeterminateIconCls={item.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  data={item}
                                                  checked={this.isItemChecked(item)}
                                                  value={Util.getValueByValueField(item, valueField, displayField)}
                                                  text={Util.getTextByDisplayField(item, displayField, valueField)}
                                                  desc={item[descriptionField] || null}
                                                  disabled={disabled || item.disabled}
                                                  isLoading={isLoading || item.isLoading}
                                                  selectMode={selectMode}
                                                  renderer={renderer}
                                                  onTouchTap={(e) => {
                                                      this.listItemTouchTapHandler(item, index);
                                                      item.onTouchTap && item.onTouchTap(e);
                                                  }}
                                                  onSelect={() => {
                                                      this.listItemSelectHandler(item, index);
                                                  }}
                                                  onDeselect={() => {
                                                      this.listItemDeselectHandler(item, index);
                                                  }}/>
                                    )
                                    :
                                    (
                                        <ListItem key={index}
                                                  index={index}
                                                  style={{height: itemHeight}}
                                                  theme={item.theme || theme}
                                                  selectTheme={item.selectTheme || selectTheme}
                                                  radioUncheckedIconCls={item.radioUncheckedIconCls || radioUncheckedIconCls}
                                                  radioCheckedIconCls={item.radioCheckedIconCls || radioCheckedIconCls}
                                                  checkboxUncheckedIconCls={item.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkboxCheckedIconCls={item.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  checkboxIndeterminateIconCls={item.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  data={item}
                                                  checked={this.isItemChecked(item)}
                                                  value={item}
                                                  text={item}
                                                  disabled={disabled}
                                                  isLoading={isLoading}
                                                  selectMode={selectMode}
                                                  renderer={renderer}
                                                  onTouchTap={() => {
                                                      this.listItemTouchTapHandler(item, index);
                                                  }}
                                                  onSelect={() => {
                                                      this.listItemSelectHandler(item, index);
                                                  }}
                                                  onDeselect={() => {
                                                      this.listItemDeselectHandler(item, index);
                                                  }}/>
                                    );

                            })
                        )
                        :
                        null
                }

                {children}

        );
    }
};

List.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The theme of the list item.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme of the list item select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Children passed into the ListItem.
     */
    data: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

        /**
         * The CSS class name of the list button.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the list button.
         */
        style: PropTypes.object,

        /**
         * The theme of the list button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the list button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The list item's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the list button. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the list item will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * If true,the element's ripple effect will be disabled.
         */
        disableTouchRipple: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        /**
         * If true,the item will have center displayed ripple effect.
         */
        rippleDisplayCenter: PropTypes.bool,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a list item touch-tapped.
         */
        onTouchTap: PropTypes.func

    }), PropTypes.string, PropTypes.number, PropTypes.symbol])).isRequired,

    /**
     * The id field name in data. (default: "id")
     */
    idField: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the list will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the list will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The mode of listItem.Can be normal,checkbox.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    shouldPreventContainerScroll: PropTypes.bool,

    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * Callback function fired when the list-item touch tap.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when the list-item select.
     */
    onItemSelect: PropTypes.func,

    /**
     * Callback function fired when the list-item deselect.
     */
    onItemDeselect: PropTypes.func,

    /**
     * Callback function fired when the list changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func

};

List.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    data: [],

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    disabled: false,
    selectMode: SelectMode.NORMAL,
    shouldPreventContainerScroll: true,

    radioUncheckedIconCls: 'fa fa-check',
    radioCheckedIconCls: 'fa fa-check',
    checkboxUncheckedIconCls: 'fa fa-square-o',
    checkboxCheckedIconCls: 'fa fa-check-square',
    checkboxIndeterminateIconCls: 'fa fa-minus-square'

};<commit_msg>update List<commit_after>/**
 * @file List component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';

import ListItem from '../_ListItem';
import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import SelectMode from '../_statics/SelectMode';
import LIST_SEPARATOR from '../_statics/ListSeparator';

export default class List extends Component {

    static SelectMode = SelectMode;
    static LIST_SEPARATOR = LIST_SEPARATOR;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: this.initValue(props)
        };

        this.initValue = ::this.initValue;
        this.isItemChecked = ::this.isItemChecked;
        this.listItemTouchTapHandler = ::this.listItemTouchTapHandler;
        this.listItemSelectHandler = ::this.listItemSelectHandler;
        this.listItemDeselectHandler = ::this.listItemDeselectHandler;
        this.wheelHandler = ::this.wheelHandler;

    }

    initValue(props) {

        if (!props) {
            return;
        }

        const {value, selectMode} = props;

        if (!selectMode) {
            return;
        }

        if (value) {
            return value;
        }

        switch (selectMode) {
            case List.SelectMode.MULTI_SELECT:
                return [];
            case List.SelectMode.SINGLE_SELECT:
                return null;
            default:
                return value;
        }

    }

    isItemChecked(item) {

        const {selectMode, valueField, displayField} = this.props,
            {value} = this.state;

        if (!value) {
            return false;
        }

        if (selectMode === List.SelectMode.MULTI_SELECT) {
            return _.isArray(value) && value.filter(valueItem => {
                    return Util.isValueEqual(valueItem, item, valueField, displayField);
                }).length > 0;
        } else if (selectMode === List.SelectMode.SINGLE_SELECT) {
            return Util.isValueEqual(value, item, valueField, displayField);
        }

    }

    listItemTouchTapHandler(value, index) {

        const {selectMode} = this.props;

        if (selectMode !== List.SelectMode.NORMAL) {
            return;
        }

        this.setState({
            value
        }, () => {
            const {onItemTouchTap, onChange} = this.props;
            onItemTouchTap && onItemTouchTap(value, index);
            onChange && onChange(value, index);
        });

    }

    listItemSelectHandler(item, index) {

        const {selectMode} = this.props;

        if (selectMode === List.SelectMode.NORMAL) {
            return;
        }

        let {value} = this.state;

        if (selectMode === List.SelectMode.MULTI_SELECT) {

            if (!value || !_.isArray(value)) {
                value = [];
            }

            value.push(item);

        } else if (selectMode === List.SelectMode.SINGLE_SELECT) {
            value = item;
        }

        this.setState({
            value
        }, () => {
            const {onItemSelect, onChange} = this.props;
            onItemSelect && onItemSelect(item, index);
            onChange && onChange(value, index);
        });

    }

    listItemDeselectHandler(item, index) {

        const {selectMode} = this.props;

        if (selectMode !== List.SelectMode.MULTI_SELECT) {
            return;
        }

        const {valueField, displayField} = this.props;
        let {value} = this.state;

        if (!value || !_.isArray(value)) {
            value = [];
        } else {
            value = value.filter(valueItem => {
                return Util.getValueByValueField(valueItem, valueField, displayField)
                    != Util.getValueByValueField(item, valueField, displayField);
            });
        }

        this.setState({
            value
        }, () => {
            const {onItemDeselect, onChange} = this.props;
            onItemDeselect && onItemDeselect(item, index);
            onChange && onChange(value, index);
        });

    }

    wheelHandler(e) {
        const {shouldPreventContainerScroll, onWheel} = this.props;
        shouldPreventContainerScroll && Event.preventContainerScroll(e);
        onWheel && onWheel(e);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: this.initValue(nextProps)
            });
        }
    }

    render() {

        const {

                children, className, style, theme, data, itemHeight,

                selectTheme, selectMode, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                idField, valueField, displayField, descriptionField, disabled, isLoading, renderer

            } = this.props,
            listClassName = (className ? ' ' + className : '');

        return (

                {
                    _.isArray(data) && data.length > 0 ?
                        (
                            data.map((item, index) => {

                                if (item === LIST_SEPARATOR) {
                                    return

;
                                }

                                return typeof item === 'object' ?
                                    (
                                        <ListItem key={item[idField] || index}
                                                  {...item}
                                                  index={index}
                                                  style={{height: itemHeight}}
                                                  theme={item.theme || theme}
                                                  selectTheme={item.selectTheme || selectTheme}
                                                  radioUncheckedIconCls={item.radioUncheckedIconCls || radioUncheckedIconCls}
                                                  radioCheckedIconCls={item.radioCheckedIconCls || radioCheckedIconCls}
                                                  checkboxUncheckedIconCls={item.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkboxCheckedIconCls={item.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  checkboxIndeterminateIconCls={item.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  data={item}
                                                  checked={this.isItemChecked(item)}
                                                  value={Util.getValueByValueField(item, valueField, displayField)}
                                                  text={Util.getTextByDisplayField(item, displayField, valueField)}
                                                  desc={item[descriptionField] || null}
                                                  disabled={disabled || item.disabled}
                                                  isLoading={isLoading || item.isLoading}
                                                  selectMode={selectMode}
                                                  renderer={renderer}
                                                  onTouchTap={(e) => {
                                                      this.listItemTouchTapHandler(item, index);
                                                      item.onTouchTap && item.onTouchTap(e);
                                                  }}
                                                  onSelect={() => {
                                                      this.listItemSelectHandler(item, index);
                                                  }}
                                                  onDeselect={() => {
                                                      this.listItemDeselectHandler(item, index);
                                                  }}/>
                                    )
                                    :
                                    (
                                        <ListItem key={index}
                                                  index={index}
                                                  style={{height: itemHeight}}
                                                  theme={item.theme || theme}
                                                  selectTheme={item.selectTheme || selectTheme}
                                                  radioUncheckedIconCls={item.radioUncheckedIconCls || radioUncheckedIconCls}
                                                  radioCheckedIconCls={item.radioCheckedIconCls || radioCheckedIconCls}
                                                  checkboxUncheckedIconCls={item.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkboxCheckedIconCls={item.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  checkboxIndeterminateIconCls={item.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  data={item}
                                                  checked={this.isItemChecked(item)}
                                                  value={item}
                                                  text={item}
                                                  disabled={disabled}
                                                  isLoading={isLoading}
                                                  selectMode={selectMode}
                                                  renderer={renderer}
                                                  onTouchTap={() => {
                                                      this.listItemTouchTapHandler(item, index);
                                                  }}
                                                  onSelect={() => {
                                                      this.listItemSelectHandler(item, index);
                                                  }}
                                                  onDeselect={() => {
                                                      this.listItemDeselectHandler(item, index);
                                                  }}/>
                                    );

                            })
                        )
                        :
                        null
                }

                {children}

        );
    }
};

List.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The theme of the list item.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme of the list item select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Children passed into the ListItem.
     */
    data: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

        /**
         * The CSS class name of the list button.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the list button.
         */
        style: PropTypes.object,

        /**
         * The theme of the list button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the list button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The list item's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the list button. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the list item will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * If true,the element's ripple effect will be disabled.
         */
        disableTouchRipple: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        /**
         * If true,the item will have center displayed ripple effect.
         */
        rippleDisplayCenter: PropTypes.bool,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a list item touch-tapped.
         */
        onTouchTap: PropTypes.func

    }), PropTypes.string, PropTypes.number, PropTypes.symbol])).isRequired,

    /**
     * The id field name in data. (default: "id")
     */
    idField: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the list will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the list will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The mode of listItem.Can be normal,checkbox.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    shouldPreventContainerScroll: PropTypes.bool,

    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * Callback function fired when the list-item touch tap.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when the list-item select.
     */
    onItemSelect: PropTypes.func,

    /**
     * Callback function fired when the list-item deselect.
     */
    onItemDeselect: PropTypes.func,

    /**
     * Callback function fired when the list changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func

};

List.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,
    selectTheme: Theme.DEFAULT,

    data: [],

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    disabled: false,
    selectMode: SelectMode.NORMAL,
    shouldPreventContainerScroll: true,

    radioUncheckedIconCls: 'fa fa-check',
    radioCheckedIconCls: 'fa fa-check',
    checkboxUncheckedIconCls: 'fa fa-square-o',
    checkboxCheckedIconCls: 'fa fa-check-square',
    checkboxIndeterminateIconCls: 'fa fa-minus-square'

};<|endoftext|>


<commit_before>import recaptcha from './recaptcha.js';

let widgetId = null;

export default {
  props: {
    key: {
      type: String,
      required: true
    },
    options: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  created() {
    recaptcha.checkRecaptchaLoad();
  },
  mounted() {
    const self = this;
    const opts = Object.assign({}, this.options, {
      callback: this.emitVerify,
      'expired-callback': this.emitExpired
    });
    recaptcha.render(this.$refs.container, this.key, opts)
      .then((id) => {
        widgetId = id;
        self.$emit('render', widgetId);
      });
  },
  methods: {
    reset() {
      recaptcha.reset(widgetId);
    },
    emitVerify(response) {
      this.$emit('verify', response);
    },
    emitExpired() {
      this.$emit('expired');
    }
  },
  events: {
    recaptchaReset() {
      this.reset();
    }
  },
  render(h) {
    return (

    );
  }
};
<commit_msg>fix: Rename prop key -> sitekey<commit_after>import recaptcha from './recaptcha.js';

let widgetId = null;

export default {
  props: {
    sitekey: {
      type: String,
      required: true
    },
    options: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  created() {
    recaptcha.checkRecaptchaLoad();
  },
  mounted() {
    const self = this;
    const opts = Object.assign({}, this.options, {
      callback: this.emitVerify,
      'expired-callback': this.emitExpired
    });
    recaptcha.render(this.$refs.container, this.key, opts)
      .then((id) => {
        widgetId = id;
        self.$emit('render', widgetId);
      });
  },
  methods: {
    reset() {
      recaptcha.reset(widgetId);
    },
    emitVerify(response) {
      this.$emit('verify', response);
    },
    emitExpired() {
      this.$emit('expired');
    }
  },
  events: {
    recaptchaReset() {
      this.reset();
    }
  },
  render(h) {
    return (

    );
  }
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import history from 'react-router/lib/hashHistory'
import moment from 'moment';

import Toaster from './views/Toaster';
import Microphone from './views/Microphone';
import RemindersList from './views/reminders/RemindersList';

import './Reminders.css';

class Reminders extends Component {
  constructor(props) {
    super(props);

    this.state = {
      reminders: [],
    };

    this.speechController = props.route.speechController;
    this.server = props.route.server;
    this.analytics = props.route.analytics;

    this.refreshInterval = null;
    this.toaster = null;
    this.microphone = null;
    this.debugEvent = this.debugEvent.bind(this);
    this.onVoiceCommand = this.onVoiceCommand.bind(this);
    this.onParsingFailure = this.onParsingFailure.bind(this);
    this.onWebPushMessage = this.onWebPushMessage.bind(this);
    this.refreshReminders = this.refreshReminders.bind(this);
    this.addReminder = this.addReminder.bind(this);

    moment.locale(navigator.languages || navigator.language || 'en-US');
  }

  componentWillMount() {
    if (typeof sessionStorage === 'undefined') {
      // The following code won't work under Node.js.
      return;
    }

    // Redirect to login if the user is not logged yet.
    if (this.server && this.server.isLoggedIn) {
      this.server.subscribeToNotifications();
      this.server.reminders.init();
    } else {
      history.push('login');
    }
  }

  componentDidMount() {
    this.refreshReminders();

    // Refresh the page every 10 seconds if idle.
    this.refreshInterval = setInterval(() => {
      this.refreshReminders()
        .then(() => {
          console.log('Reminders loaded');
        });
    }, 10 * 1000);

    this.speechController.on('speechrecognitionstart', this.debugEvent);
    this.speechController.on('speechrecognitionstop', this.debugEvent);
    this.speechController.on('reminder', this.debugEvent);

    this.speechController.on('reminder', this.onVoiceCommand);
    this.speechController.on('parsing-failed', this.onParsingFailure);
    this.server.on('push-message', this.onWebPushMessage);
  }

  componentWillUnmount() {
    clearInterval(this.refreshInterval);

    this.speechController.off('speechrecognitionstart', this.debugEvent);
    this.speechController.off('speechrecognitionstop', this.debugEvent);
    this.speechController.off('reminder', this.debugEvent);

    this.speechController.off('reminder', this.onVoiceCommand);
    this.speechController.off('parsing-failed', this.onParsingFailure);
    this.server.off('push-message', this.onWebPushMessage);
  }

  debugEvent(evt) {
    if (evt.result !== undefined) {
      console.log(evt.type, evt.result);
      return;
    }

    console.log(evt.type);
  }

  refreshReminders() {
    // @todo Add a loader.
    return this.server.reminders.getAll()
      .then((reminders) => {
        this.setState({ reminders });
      });
  }

  addReminder(reminder) {
    const reminders = this.state.reminders;
    reminders.push(reminder);

    this.setState({ reminders });
  }

  onVoiceCommand({ result }) {
    this.microphone.stopListeningToSpeech();

    switch (result.intent) {
      case 'reminder':
        return this.onReminder(result);

      case 'query':
        return this.onQuery(result);

      default: {
        console.info('Only intent of type `reminder` are supported now.');
        const message = 'I can only understand new reminders. ' +
          'Try saying "Remind me to go the hairdresser tomorrow at 5pm".';
        this.toaster.info(message);
        this.speechController.speak(message)
          .then(() => {
            this.toaster.hide();
          });
        break;
      }
    }
  }

  onReminder({ recipients, action, due, confirmation }) {
    // @todo Nice to have: optimistic update.
    this.server.reminders
      .set({
        recipients,
        action,
        due,
      })
      .then((reminder) => {
        this.analytics.event('reminders', 'create');

        this.addReminder(reminder);

        this.toaster.success(confirmation);
        this.speechController.speak(confirmation)
          .then(() => {
            console.log('Utterance terminated.');
            this.toaster.hide();
          });
      })
      .catch((res) => {
        console.warn('Saving the reminder failed.', res);

        this.analytics.event('reminders', 'error', 'create-failed');

        const message = 'This reminder could not be saved. ' +
          'Please try again later.';
        this.toaster.warning(message);
        this.speechController.speak(message)
          .then(() => {
            this.toaster.hide();
          });
      });
  }

  onQuery({ recipients, due }) {
    if (recipients.length !== 1) {
      console.error('Query must have only 1 recipient.');
    }

    // Reminders last for 3 hours by convention.
    const reminder = this.state.reminders.find((reminder) => {
      return reminder.recipients.includes(recipients[0])
        && reminder.due <= due && due <= (reminder.due + 3 * 60 * 60 * 1000);
    });

    let message;

    if (!reminder) {
      message = 'I don\'t have any appointment at that time.';
    } else {
      message = `You're busy with ${reminder.action}.`;
    }

    this.toaster.warning(message);
    this.speechController.speak(message)
      .then(() => {
        console.log('Utterance terminated.');
        this.toaster.hide();
      });
  }

  onParsingFailure() {
    this.microphone.stopListeningToSpeech();

    this.analytics.event('reminders', 'parsing-failed');

    const message = 'I did not understand that. Can you repeat?';
    this.toaster.warning(message);
    this.speechController.speak(message)
      .then(() => {
        console.log('Utterance terminated.');
        this.toaster.hide();
      });
  }

  onWebPushMessage(message) {
    const id = message.fullMessage.id;

    // We don't want to delete them, merely remove it from our local state.
    // At reload, we shouldn't get it because their status changed server-side
    // too.
    const reminders = this.state.reminders
      .filter((reminder) => reminder.id !== id);
    this.setState({ reminders });
  }

  // @todo Add a different view when there's no reminders:
  render() {
    return (

        <Toaster ref={(t) => this.toaster = t}/>
        <Microphone ref={(t) => this.microphone = t}
                    speechController={this.speechController}
                    server={this.server}
                    analytics={this.analytics}/>
        <RemindersList reminders={this.state.reminders}
                       server={this.server}
                       analytics={this.analytics}
                       refreshReminders={this.refreshReminders}/>

    );
  }
}

export default Reminders;
<commit_msg>Hardcode moment locale to en-US<commit_after>import React, { Component } from 'react';
import history from 'react-router/lib/hashHistory'
import moment from 'moment';

import Toaster from './views/Toaster';
import Microphone from './views/Microphone';
import RemindersList from './views/reminders/RemindersList';

import './Reminders.css';

class Reminders extends Component {
  constructor(props) {
    super(props);

    moment.locale('en-US');

    this.state = {
      reminders: [],
    };

    this.speechController = props.route.speechController;
    this.server = props.route.server;
    this.analytics = props.route.analytics;

    this.refreshInterval = null;
    this.toaster = null;
    this.microphone = null;
    this.debugEvent = this.debugEvent.bind(this);
    this.onVoiceCommand = this.onVoiceCommand.bind(this);
    this.onParsingFailure = this.onParsingFailure.bind(this);
    this.onWebPushMessage = this.onWebPushMessage.bind(this);
    this.refreshReminders = this.refreshReminders.bind(this);
    this.addReminder = this.addReminder.bind(this);
  }

  componentWillMount() {
    if (typeof sessionStorage === 'undefined') {
      // The following code won't work under Node.js.
      return;
    }

    // Redirect to login if the user is not logged yet.
    if (this.server && this.server.isLoggedIn) {
      this.server.subscribeToNotifications();
      this.server.reminders.init();
    } else {
      history.push('login');
    }
  }

  componentDidMount() {
    this.refreshReminders();

    // Refresh the page every 10 seconds if idle.
    this.refreshInterval = setInterval(() => {
      this.refreshReminders()
        .then(() => {
          console.log('Reminders loaded');
        });
    }, 10 * 1000);

    this.speechController.on('speechrecognitionstart', this.debugEvent);
    this.speechController.on('speechrecognitionstop', this.debugEvent);
    this.speechController.on('reminder', this.debugEvent);

    this.speechController.on('reminder', this.onVoiceCommand);
    this.speechController.on('parsing-failed', this.onParsingFailure);
    this.server.on('push-message', this.onWebPushMessage);
  }

  componentWillUnmount() {
    clearInterval(this.refreshInterval);

    this.speechController.off('speechrecognitionstart', this.debugEvent);
    this.speechController.off('speechrecognitionstop', this.debugEvent);
    this.speechController.off('reminder', this.debugEvent);

    this.speechController.off('reminder', this.onVoiceCommand);
    this.speechController.off('parsing-failed', this.onParsingFailure);
    this.server.off('push-message', this.onWebPushMessage);
  }

  debugEvent(evt) {
    if (evt.result !== undefined) {
      console.log(evt.type, evt.result);
      return;
    }

    console.log(evt.type);
  }

  refreshReminders() {
    // @todo Add a loader.
    return this.server.reminders.getAll()
      .then((reminders) => {
        this.setState({ reminders });
      });
  }

  addReminder(reminder) {
    const reminders = this.state.reminders;
    reminders.push(reminder);

    this.setState({ reminders });
  }

  onVoiceCommand({ result }) {
    this.microphone.stopListeningToSpeech();

    switch (result.intent) {
      case 'reminder':
        return this.onReminder(result);

      case 'query':
        return this.onQuery(result);

      default: {
        console.info('Only intent of type `reminder` are supported now.');
        const message = 'I can only understand new reminders. ' +
          'Try saying "Remind me to go the hairdresser tomorrow at 5pm".';
        this.toaster.info(message);
        this.speechController.speak(message)
          .then(() => {
            this.toaster.hide();
          });
        break;
      }
    }
  }

  onReminder({ recipients, action, due, confirmation }) {
    // @todo Nice to have: optimistic update.
    this.server.reminders
      .set({
        recipients,
        action,
        due,
      })
      .then((reminder) => {
        this.analytics.event('reminders', 'create');

        this.addReminder(reminder);

        this.toaster.success(confirmation);
        this.speechController.speak(confirmation)
          .then(() => {
            console.log('Utterance terminated.');
            this.toaster.hide();
          });
      })
      .catch((res) => {
        console.warn('Saving the reminder failed.', res);

        this.analytics.event('reminders', 'error', 'create-failed');

        const message = 'This reminder could not be saved. ' +
          'Please try again later.';
        this.toaster.warning(message);
        this.speechController.speak(message)
          .then(() => {
            this.toaster.hide();
          });
      });
  }

  onQuery({ recipients, due }) {
    if (recipients.length !== 1) {
      console.error('Query must have only 1 recipient.');
    }

    // Reminders last for 3 hours by convention.
    const reminder = this.state.reminders.find((reminder) => {
      return reminder.recipients.includes(recipients[0])
        && reminder.due <= due && due <= (reminder.due + 3 * 60 * 60 * 1000);
    });

    let message;

    if (!reminder) {
      message = 'I don\'t have any appointment at that time.';
    } else {
      message = `You're busy with ${reminder.action}.`;
    }

    this.toaster.warning(message);
    this.speechController.speak(message)
      .then(() => {
        console.log('Utterance terminated.');
        this.toaster.hide();
      });
  }

  onParsingFailure() {
    this.microphone.stopListeningToSpeech();

    this.analytics.event('reminders', 'parsing-failed');

    const message = 'I did not understand that. Can you repeat?';
    this.toaster.warning(message);
    this.speechController.speak(message)
      .then(() => {
        console.log('Utterance terminated.');
        this.toaster.hide();
      });
  }

  onWebPushMessage(message) {
    const id = message.fullMessage.id;

    // We don't want to delete them, merely remove it from our local state.
    // At reload, we shouldn't get it because their status changed server-side
    // too.
    const reminders = this.state.reminders
      .filter((reminder) => reminder.id !== id);
    this.setState({ reminders });
  }

  // @todo Add a different view when there's no reminders:
  render() {
    return (

        <Toaster ref={(t) => this.toaster = t}/>
        <Microphone ref={(t) => this.microphone = t}
                    speechController={this.speechController}
                    server={this.server}
                    analytics={this.analytics}/>
        <RemindersList reminders={this.state.reminders}
                       server={this.server}
                       analytics={this.analytics}
                       refreshReminders={this.refreshReminders}/>

    );
  }
}

export default Reminders;
<|endoftext|>


<commit_before>const reservedTagList = new Set([
  'annotation-xml',
  'color-profile',
  'font-face',
  'font-face-src',
  'font-face-uri',
  'font-face-format',
  'font-face-name',
  'missing-glyph',
]);

/**
 * @param {string} localName
 * @returns {boolean}
 */
export function isValidCustomElementName(localName) {
  const reserved = reservedTagList.has(localName);
  const validForm = /^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(localName);
  return !reserved && validForm;
}

/**
 * @private
 * @param {!Node} node
 * @return {boolean}
 */
export function isConnected(node) {
  // Use `Node#isConnected`, if defined.
  const nativeValue = node.isConnected;
  if (nativeValue !== undefined) {
    return nativeValue;
  }

  while (node && !(node.__CE_isImportDocument || node instanceof Document)) {
    node = node.parentNode || (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? node.host : undefined);
  }
  return !!(node && (node.__CE_isImportDocument || node instanceof Document));
}

/**
 * @param {!Node} root
 * @param {!function(!Element)} callback
 */
export function walkDeepDescendantElements(root, callback) {
  // IE throws on `walker.nextNode()` if the root given to TreeWalker is a text node.
  if (root.nodeType === Node.TEXT_NODE) {
    return;
  }

  const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
  do {
    const element = /** @type {!Element} */ (walker.currentNode);
    callback(element);

    // Ignore descendants of link elements to prevent attempting to traverse
    // elements created by the HTML Imports polyfill. When upgrading a tree,
    // `CustomElementInternals#upgradeTree` by walks the `import` property of
    // import links, which will point to either the true imported document
    // (native) or the descendant containing the elements created to emulate the
    // imported document (polyfill).
    if (element.localName === 'link') {
      let context = element;
      let next = context.nextSibling;
      while (!next && context !== root) {
        context = context.parentNode;
        next = context.nextSibling;
      }

      if (!next || context === root) return;

      walker.currentNode = next;
      walker.previousNode();
      continue;
    }

    const shadowRoot = element.__CE_shadowRoot;
    if (shadowRoot) {
      const children = shadowRoot.children;
      for (var i = 0; i < children.length; i++) {
        walkDeepDescendantElements(children[i], callback);
      }
    }
  } while (walker.nextNode());
}

/**
 * Used to suppress Closure's "Modifying the prototype is only allowed if the
 * constructor is in the same scope" warning without using
 * `@suppress {newCheckTypes, duplicate}` because `newCheckTypes` is too broad.
 *
 * @param {!Object} destination
 * @param {string} name
 * @param {*} value
 */
export function setPropertyUnchecked(destination, name, value) {
  destination[name] = value;
}
<commit_msg>`Utilities.walkDeepDescendantElements`: Remove unnecessary logic for skipping link descendants.<commit_after>const reservedTagList = new Set([
  'annotation-xml',
  'color-profile',
  'font-face',
  'font-face-src',
  'font-face-uri',
  'font-face-format',
  'font-face-name',
  'missing-glyph',
]);

/**
 * @param {string} localName
 * @returns {boolean}
 */
export function isValidCustomElementName(localName) {
  const reserved = reservedTagList.has(localName);
  const validForm = /^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(localName);
  return !reserved && validForm;
}

/**
 * @private
 * @param {!Node} node
 * @return {boolean}
 */
export function isConnected(node) {
  // Use `Node#isConnected`, if defined.
  const nativeValue = node.isConnected;
  if (nativeValue !== undefined) {
    return nativeValue;
  }

  while (node && !(node.__CE_isImportDocument || node instanceof Document)) {
    node = node.parentNode || (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? node.host : undefined);
  }
  return !!(node && (node.__CE_isImportDocument || node instanceof Document));
}

/**
 * @param {!Node} root
 * @param {!function(!Element)} callback
 */
export function walkDeepDescendantElements(root, callback) {
  // IE throws on `walker.nextNode()` if the root given to TreeWalker is a text node.
  if (root.nodeType === Node.TEXT_NODE) {
    return;
  }

  const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
  do {
    const element = /** @type {!Element} */ (walker.currentNode);
    callback(element);

    // Ignore descendants of link elements to prevent attempting to traverse
    // elements created by the HTML Imports polyfill. When upgrading a tree,
    // `CustomElementInternals#upgradeTree` by walks the `import` property of
    // import links, which will point to either the true imported document
    // (native) or the descendant containing the elements created to emulate the
    // imported document (polyfill).
    if (element.localName === 'link') {
      walker.currentNode = element.lastChild || element;
      continue;
    }

    const shadowRoot = element.__CE_shadowRoot;
    if (shadowRoot) {
      const children = shadowRoot.children;
      for (var i = 0; i < children.length; i++) {
        walkDeepDescendantElements(children[i], callback);
      }
    }
  } while (walker.nextNode());
}

/**
 * Used to suppress Closure's "Modifying the prototype is only allowed if the
 * constructor is in the same scope" warning without using
 * `@suppress {newCheckTypes, duplicate}` because `newCheckTypes` is too broad.
 *
 * @param {!Object} destination
 * @param {string} name
 * @param {*} value
 */
export function setPropertyUnchecked(destination, name, value) {
  destination[name] = value;
}
<|endoftext|>


<commit_before>Mac OS X        	2ATTRFFcom.apple.quarantineq/0001;583c28ab;Google\x20Chrome;96E2A0DA-9824-47DF-972C-91E31907A225<commit_msg>Delete ._npm.js<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import XR from './XR';
import XRSession from './XRSession';
import XRFrame from './XRFrame';
import XRView from './XRView';
import XRViewport from './XRViewport';
import XRViewerPose from './XRViewerPose';
import XRInputSource from './XRInputSource';
import XRWebGLLayer from './XRWebGLLayer';
import XRSpace from './XRSpace';
import XRReferenceSpace from './XRReferenceSpace';
import XRRenderState from './XRRenderState';
import XRRigidTransform from './XRRigidTransform';
import XRPose from './XRPose';

/**
 * Everything exposed here will also be attached to the window
 */
export default {
  XR,
  XRSession,
  XRFrame,
  XRView,
  XRViewport,
  XRViewerPose,
  XRWebGLLayer,
  XRSpace,
  XRReferenceSpace,
  XRInputSource,
  XRRenderState,
  XRRigidTransform,
  XRPose,
};
<commit_msg>Expose XRInputSourceEvent<commit_after>/*
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import XR from './XR';
import XRSession from './XRSession';
import XRFrame from './XRFrame';
import XRView from './XRView';
import XRViewport from './XRViewport';
import XRViewerPose from './XRViewerPose';
import XRInputSource from './XRInputSource';
import XRInputSourceEvent from './XRInputSourceEvent';
import XRWebGLLayer from './XRWebGLLayer';
import XRSpace from './XRSpace';
import XRReferenceSpace from './XRReferenceSpace';
import XRRenderState from './XRRenderState';
import XRRigidTransform from './XRRigidTransform';
import XRPose from './XRPose';

/**
 * Everything exposed here will also be attached to the window
 */
export default {
  XR,
  XRSession,
  XRFrame,
  XRView,
  XRViewport,
  XRViewerPose,
  XRWebGLLayer,
  XRSpace,
  XRReferenceSpace,
  XRInputSource,
  XRInputSourceEvent,
  XRRenderState,
  XRRigidTransform,
  XRPose,
};
<|endoftext|>


<commit_before>jest.unmock('../lib/event-emitter')

var EventEmitter = require('../lib/event-emitter')

test('should register listener', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  expect(emitter.listeners).toEqual([
    {
      eventName: 'event',
      handler: handler
    }
  ])
});

test('should register listener', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  expect(emitter.listeners).toEqual([
    {
      eventName: 'event',
      handler: handler
    }
  ])
});

test('should register fallback', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.fallback('event', handler)
  expect(emitter.fallbacks['event']).toEqual(handler)
});

// EventEmitter.prototype.emit = function(eventName) {
//   var event = new Event(eventName, eventListeners, args)
//   event.next(function(eventName) {
//
//     var handler = emitter.unhandledHandlers[eventName]
//
//     if (handler) {
//       handler.apply(this, args)
//     } else {
//       console.error('Unhandled event ' + eventName);
//     }
//   })
// }
//

test('should emit event', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  var event = emitter.emit('event')

  expect(event.eventName).toEqual('event')
});

test('should emit event with correct handlers', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()
  var handler2 = jest.fn()

  emitter.addListener('event', handler)
  emitter.addListener('event2', handler2)
  var event = emitter.emit('event')

  expect(event.remainingListeners).toEqual([handler])
});

test('should emit event with correct arguments', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  var event = emitter.emit('event', 1)

  expect(event.args).toEqual([
    1,
    jest.fn()
  ])
});
//
// test('should use fallback for event with no handlers', () => {
//   var emitter = new EventEmitter()
//   var handler = jest.fn()
//
//   emitter.fallback('event', handler)
//   expect(emitter.fallbacks['event']).toEqual(handler)
// });
//
// test('should log error for event with no handlers or fallback', () => {
//   var emitter = new EventEmitter()
//   var handler = jest.fn()
//
//   emitter.fallback('event', handler)
//   expect(emitter.fallbacks['event']).toEqual(handler)
// });
<commit_msg>Args pass.<commit_after>jest.unmock('../lib/event-emitter')

var EventEmitter = require('../lib/event-emitter')

test('should register listener', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  expect(emitter.listeners).toEqual([
    {
      eventName: 'event',
      handler: handler
    }
  ])
});

test('should register listener', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  expect(emitter.listeners).toEqual([
    {
      eventName: 'event',
      handler: handler
    }
  ])
});

test('should register fallback', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.fallback('event', handler)
  expect(emitter.fallbacks['event']).toEqual(handler)
});

// EventEmitter.prototype.emit = function(eventName) {
//   var event = new Event(eventName, eventListeners, args)
//   event.next(function(eventName) {
//
//     var handler = emitter.unhandledHandlers[eventName]
//
//     if (handler) {
//       handler.apply(this, args)
//     } else {
//       console.error('Unhandled event ' + eventName);
//     }
//   })
// }
//

test('should emit event', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  var event = emitter.emit('event')

  expect(event.eventName).toEqual('event')
});

test('should emit event with correct handlers', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()
  var handler2 = jest.fn()

  emitter.addListener('event', handler)
  emitter.addListener('event2', handler2)
  var event = emitter.emit('event')

  expect(event.remainingListeners).toEqual([handler])
});

test('should emit event with correct arguments', () => {
  var emitter = new EventEmitter()
  var handler = jest.fn()

  emitter.addListener('event', handler)
  var event = emitter.emit('event', 1)

  expect(event.args).toEqual([
    1
  ])
});
//
// test('should use fallback for event with no handlers', () => {
//   var emitter = new EventEmitter()
//   var handler = jest.fn()
//
//   emitter.fallback('event', handler)
//   expect(emitter.fallbacks['event']).toEqual(handler)
// });
//
// test('should log error for event with no handlers or fallback', () => {
//   var emitter = new EventEmitter()
//   var handler = jest.fn()
//
//   emitter.fallback('event', handler)
//   expect(emitter.fallbacks['event']).toEqual(handler)
// });
<|endoftext|>


<commit_before>import { put, takeLatest } from 'redux-saga/effects';
import * as types from 'models/actions/actionTypes';
import { requestCurrentPage, requestNextPage } from 'models/sagas/players/requests';

export function* build(action) {
   if (action.payload) {
      const entities = action.payload.entities;

      yield put({ type: types.CREATE_ABILITIES, payload: entities.abilities });
      yield put({ type: types.CREATE_AFFINITIES, payload: entities.affinities });
      yield put({ type: types.CREATE_PLAYERS, payload: entities.players });
   } else {
      console.error('Missing payload');
   }
}
<commit_msg>Fixed import<commit_after>import { put } from 'redux-saga/effects';
import * as types from 'models/actions/actionTypes';

export function* build(action) {
   if (action.payload) {
      const entities = action.payload.entities;

      yield put({ type: types.CREATE_ABILITIES, payload: entities.abilities });
      yield put({ type: types.CREATE_AFFINITIES, payload: entities.affinities });
      yield put({ type: types.CREATE_PLAYERS, payload: entities.players });
   } else {
      console.error('Missing payload');
   }
}
<|endoftext|>


<commit_before>// Redis related
var redis = require("redis");
var redisClient;

if (process.env["REDISTOGO_URL"]) {
    // TODO: redistogo connection
	// inside if statement
	var rtg   = require("url").parse(process.env["REDISTOGO_URL"]);
	redisClient = redis.createClient(rtg.port, rtg.hostname);
	redisClient.auth(rtg.auth.split(":")[1]);

} else {
    redisClient = redis.createClient();
}

var Rx = require('Rx');

// Returns an observable for a redis value for a given key
function valueForKey(key){
	return Rx.Observable.create(function(observer){
		redisClient.get(key, function(err, reply){
			if (err)
				observer.error(err);
			else {
				var jsonReply = JSON.parse(reply)
				observer.onNext(jsonReply);
				observer.onCompleted();
			}
		})
	})
}

// Returns an observable that retuns the value once it's been set in redis
function setValueForKey(key, value){
	return Rx.Observable.create(function(observer){
		var jsonValue = JSON.stringify(value);
		redisClient.set(key, jsonValue, function(error){
			if (error)
				observer.error(error)
			else {
				observer.onNext(value);
				observer.onCompleted();
			}

		});
	})
}

module.exports.valueForKey = valueForKey;
module.exports.setValueForKey = setValueForKey;<commit_msg>Fixing rx<commit_after>// Redis related
var redis = require("redis");
var redisClient;

if (process.env["REDISTOGO_URL"]) {
    // TODO: redistogo connection
	// inside if statement
	var rtg   = require("url").parse(process.env["REDISTOGO_URL"]);
	redisClient = redis.createClient(rtg.port, rtg.hostname);
	redisClient.auth(rtg.auth.split(":")[1]);

} else {
    redisClient = redis.createClient();
}

var Rx = require('rx');

// Returns an observable for a redis value for a given key
function valueForKey(key){
	return Rx.Observable.create(function(observer){
		redisClient.get(key, function(err, reply){
			if (err)
				observer.error(err);
			else {
				var jsonReply = JSON.parse(reply)
				observer.onNext(jsonReply);
				observer.onCompleted();
			}
		})
	})
}

// Returns an observable that retuns the value once it's been set in redis
function setValueForKey(key, value){
	return Rx.Observable.create(function(observer){
		var jsonValue = JSON.stringify(value);
		redisClient.set(key, jsonValue, function(error){
			if (error)
				observer.error(error)
			else {
				observer.onNext(value);
				observer.onCompleted();
			}

		});
	})
}

module.exports.valueForKey = valueForKey;
module.exports.setValueForKey = setValueForKey;<|endoftext|>


<commit_before>const path = require('path')
const camelcase = require('lodash.camelcase')
const rollupBabel = require('rollup-plugin-babel')
const commonjs = require('rollup-plugin-commonjs')
const nodeResolve = require('rollup-plugin-node-resolve')
const json = require('rollup-plugin-json')
const replace = require('rollup-plugin-replace')
const uglify = require('rollup-plugin-uglify')
const nodeBuiltIns = require('rollup-plugin-node-builtins')
const nodeGlobals = require('rollup-plugin-node-globals')
const omit = require('lodash.omit')
const {pkg, hasFile, hasPkgProp, parseEnv, ifFile} = require('../utils')

const here = p => path.join(__dirname, p)
const capitalize = s => s[0].toUpperCase() + s.slice(1)

const minify = parseEnv('BUILD_MINIFY', false)
const format = process.env.BUILD_FORMAT
const isPreact = parseEnv('BUILD_PREACT', false)
const isNode = parseEnv('BUILD_NODE', false)
const name = process.env.BUILD_NAME || capitalize(camelcase(pkg.name))

const defaultGlobals = Object.keys(pkg.peerDependencies || {}).reduce(
  (deps, dep) => {
    deps[dep] = capitalize(camelcase(dep))
    return deps
  },
  {},
)

const defaultExternal = Object.keys(pkg.peerDependencies || {})

const input =
  process.env.BUILD_INPUT ||
  ifFile(`src/${format}-entry.js`, `src/${format}-entry.js`, 'src/index.js')

const filenameSuffix = process.env.BUILD_FILENAME_SUFFIX || ''
const filenamePrefix =
  process.env.BUILD_FILENAME_PREFIX || (isPreact ? 'preact/' : '')
const globals = parseEnv(
  'BUILD_GLOBALS',
  isPreact ? Object.assign(defaultGlobals, {preact: 'preact'}) : defaultGlobals,
)
const external = parseEnv(
  'BUILD_EXTERNAL',
  isPreact ? defaultExternal.concat(['preact', 'prop-types']) : defaultExternal,
).filter((e, i, arry) => arry.indexOf(e) === i)

if (isPreact) {
  delete globals.react
  delete globals['prop-types'] // TODO: is this necessary?
  external.splice(external.indexOf('react'), 1)
}

const externalPattern = new RegExp(`^(${external.join('|')})($|/)`)

const esm = format === 'esm'
const umd = format === 'umd'

const filename = [
  pkg.name,
  filenameSuffix,
  `.${format}`,
  minify ? '.min' : null,
  '.js',
]
  .filter(Boolean)
  .join('')

const filepath = path.join(
  ...[filenamePrefix, 'dist', filename].filter(Boolean),
)

const output = [
  {
    name,
    file: filepath,
    format: esm ? 'es' : format,
    exports: esm ? 'named' : 'default',
    globals,
  },
]

const useBuiltinConfig = !hasFile('.babelrc') && !hasPkgProp('babel')
const babelPresets = useBuiltinConfig ? [here('../config/babelrc.js')] : []

const replacements = Object.entries(
  umd ? process.env : omit(process.env, ['NODE_ENV']),
).reduce((acc, [key, value]) => {
  let val
  if (value === 'true' || value === 'false' || Number.isInteger(+value)) {
    val = value
  } else {
    val = JSON.stringify(value)
  }
  acc[`process.env.${key}`] = val
  return acc
}, {})

module.exports = {
  input,
  output,
  external: id => externalPattern.test(id),
  plugins: [
    isNode ? nodeBuiltIns() : null,
    isNode ? nodeGlobals() : null,
    nodeResolve({preferBuiltins: isNode, jsnext: true, main: true}),
    commonjs({include: 'node_modules/**'}),
    json(),
    rollupBabel({
      exclude: 'node_modules/**',
      presets: babelPresets,
      babelrc: true,
    }),
    replace(replacements),
    minify ? uglify() : null,
  ].filter(Boolean),
}
<commit_msg>fix(rollup): fix predicate function for empty external lists (#37)<commit_after>const path = require('path')
const camelcase = require('lodash.camelcase')
const rollupBabel = require('rollup-plugin-babel')
const commonjs = require('rollup-plugin-commonjs')
const nodeResolve = require('rollup-plugin-node-resolve')
const json = require('rollup-plugin-json')
const replace = require('rollup-plugin-replace')
const uglify = require('rollup-plugin-uglify')
const nodeBuiltIns = require('rollup-plugin-node-builtins')
const nodeGlobals = require('rollup-plugin-node-globals')
const omit = require('lodash.omit')
const {pkg, hasFile, hasPkgProp, parseEnv, ifFile} = require('../utils')

const here = p => path.join(__dirname, p)
const capitalize = s => s[0].toUpperCase() + s.slice(1)

const minify = parseEnv('BUILD_MINIFY', false)
const format = process.env.BUILD_FORMAT
const isPreact = parseEnv('BUILD_PREACT', false)
const isNode = parseEnv('BUILD_NODE', false)
const name = process.env.BUILD_NAME || capitalize(camelcase(pkg.name))

const defaultGlobals = Object.keys(pkg.peerDependencies || {}).reduce(
  (deps, dep) => {
    deps[dep] = capitalize(camelcase(dep))
    return deps
  },
  {},
)

const defaultExternal = Object.keys(pkg.peerDependencies || {})

const input =
  process.env.BUILD_INPUT ||
  ifFile(`src/${format}-entry.js`, `src/${format}-entry.js`, 'src/index.js')

const filenameSuffix = process.env.BUILD_FILENAME_SUFFIX || ''
const filenamePrefix =
  process.env.BUILD_FILENAME_PREFIX || (isPreact ? 'preact/' : '')
const globals = parseEnv(
  'BUILD_GLOBALS',
  isPreact ? Object.assign(defaultGlobals, {preact: 'preact'}) : defaultGlobals,
)
const external = parseEnv(
  'BUILD_EXTERNAL',
  isPreact ? defaultExternal.concat(['preact', 'prop-types']) : defaultExternal,
).filter((e, i, arry) => arry.indexOf(e) === i)

if (isPreact) {
  delete globals.react
  delete globals['prop-types'] // TODO: is this necessary?
  external.splice(external.indexOf('react'), 1)
}

const externalPattern = new RegExp(`^(${external.join('|')})($|/)`)
const externalPredicate =
  external.length === 0 ? () => false : id => externalPattern.test(id)

const esm = format === 'esm'
const umd = format === 'umd'

const filename = [
  pkg.name,
  filenameSuffix,
  `.${format}`,
  minify ? '.min' : null,
  '.js',
]
  .filter(Boolean)
  .join('')

const filepath = path.join(
  ...[filenamePrefix, 'dist', filename].filter(Boolean),
)

const output = [
  {
    name,
    file: filepath,
    format: esm ? 'es' : format,
    exports: esm ? 'named' : 'default',
    globals,
  },
]

const useBuiltinConfig = !hasFile('.babelrc') && !hasPkgProp('babel')
const babelPresets = useBuiltinConfig ? [here('../config/babelrc.js')] : []

const replacements = Object.entries(
  umd ? process.env : omit(process.env, ['NODE_ENV']),
).reduce((acc, [key, value]) => {
  let val
  if (value === 'true' || value === 'false' || Number.isInteger(+value)) {
    val = value
  } else {
    val = JSON.stringify(value)
  }
  acc[`process.env.${key}`] = val
  return acc
}, {})

module.exports = {
  input,
  output,
  external: externalPredicate,
  plugins: [
    isNode ? nodeBuiltIns() : null,
    isNode ? nodeGlobals() : null,
    nodeResolve({preferBuiltins: isNode, jsnext: true, main: true}),
    commonjs({include: 'node_modules/**'}),
    json(),
    rollupBabel({
      exclude: 'node_modules/**',
      presets: babelPresets,
      babelrc: true,
    }),
    replace(replacements),
    minify ? uglify() : null,
  ].filter(Boolean),
}
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import "./Case.css";
import { injectIntl, intlShape } from "react-intl";
import { Link } from "react-router";
import api from "../../utils/api";
import moment from "moment";
import { Container, Row, Col } from "reactstrap";
import CountryMap from "../../components/CountryMap";
import ItemGallery from "./ItemGallery";
import FloatingActionButton from "material-ui/FloatingActionButton";
import ContentPencil from "material-ui/svg-icons/image/edit";
import BookmarkToggle from "../../components/BookmarkToggle";
import caseIconSettings from "../../img/pp-case-icon-settings.svg";
import caseIconFB from "../../img/pp-case-icon-fb.svg";
import caseIconTW from "../../img/pp-case-icon-tw.svg";
import caseIconShare from "../../img/pp-case-icon-share.svg";

class SearchLink extends React.Component {
  render() {
    let tag = this.props.tag;
    let value = this.props.value;
    let locale = this.props.locale;
    if (tag && value && locale) {
      let mapping = {};
      mapping[tag] = value;
      return (
        <Link
          to={{
            pathname: "/" + locale + "/search",
            query: mapping
          }}
        >
          {value}
        </Link>
      );
    } else {
      return
None specified
; // L10N
    }
  }
}

function mapStateToProps({ auth }) {
  const { isAuthenticated } = auth;

  return {
    isAuthenticated
  };
}

export class Case extends React.Component {
  componentWillMount() {
    let component = this;
    let isAuthenticated = this.props.isAuthenticated;
    api.fetchCaseById(this.props.params.nodeID).then(function(the_case) {
      component.setState({
        data: the_case,
        htmlBody: the_case.body,
        isAuthenticated: isAuthenticated
      });
    });
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      isAuthenticated: nextProps.isAuthenticated
    });
  }

  getInnerHTML() {
    let input = "";
    if (this.state && this.state.htmlBody) {
      input = this.state.htmlBody;
    }
    return { __html: input };
  }

  render() {
    if (this.state && this.state.data) {
      const locale = this.props.intl.locale;
      const isAuthenticated = this.props.isAuthenticated;
      let intl = this.props.intl;
      let caseObject = this.state.data;
      let bookmarked = isAuthenticated && caseObject.bookmarked;
      let issue = caseObject.issue;
      let audience = (
        <SearchLink
          locale={locale}
          tag="communication_with_audience"
          value={caseObject.communication_with_audience}
        />
      );
      let tags =
;
      if (caseObject.tags) {
        tags = caseObject.tags.map(tag => (
          <SearchLink key={tag} locale={locale} tag="tag" value={tag} />
        ));
      }
      let communication_mode = (
        <SearchLink
          locale={locale}
          tag="communication_mode"
          value={caseObject.communication_mode}
        />
      );
      let decision_method = (
        <SearchLink
          locale={locale}
          tag="decision_method"
          value={caseObject.decision_method}
        />
      );
      let facetoface = caseObject.facetoface_online_or_both;
      if (!facetoface) {
        facetoface = "facetoface_not_specified";
      } else {
        facetoface = "facetoface_" + facetoface;
      }
      facetoface = intl.formatMessage({ id: facetoface });
      facetoface = (
        <SearchLink
          locale={locale}
          tag="facetoface_online_or_both"
          value={facetoface}
        />
      );

      let facilitated = intl.formatMessage({
        id: caseObject.facilitated.toLowerCase()
      });
      facilitated = (
        <SearchLink locale={locale} tag="facilitated" value={facilitated} />
      );

      let voting = intl.formatMessage({ id: caseObject.voting });
      voting = <SearchLink locale={locale} tag="voting" value={voting} />;

      let numberDays = caseObject.number_of_meeting_days;
      numberDays = (
        <SearchLink
          locale={locale}
          tag="number_of_meeting_days"
          value={numberDays}
        />
      );

      let post_date = moment(caseObject.post_date).format("LL");
      let updated_date = moment(caseObject.updated_date).format("LL");
      let first_author = caseObject.authors[0];
      let first_author_url = "/" + locale + "/users/" + first_author.user_id;
      let first_author_name = first_author.name;
      let last_author = caseObject.authors.slice(-1)[0];
      let last_author_name = last_author.name;
      let last_author_url = "/" + locale + "/users/" + last_author.user_id;
      let id = this.props.params.nodeID;
      let editLinkUrl = `/${locale}/case/${id}/edit`;
      let awsUrl = process.env.REACT_APP_ASSETS_URL;
      let theLength = "";
      let pics = [];
      if (caseObject && caseObject.lead_image) {
        pics.push(awsUrl + encodeURIComponent(caseObject.lead_image.url));
      }
      if (caseObject && caseObject.other_images.length) {
        theLength = caseObject.other_images;
        Object.keys(theLength).forEach(function(key) {
          let obj = theLength[key];
          pics.push(awsUrl + encodeURIComponent(obj.url));
        });
      }

      return (

            <Container className="detailed-case-component" fluid={true}>
              <Row>
                <Col md="3" className="hidden-sm-down sidepanel hidden-sm-down">
                  <CountryMap
                    city={caseObject.location.city}
                    countrycode={caseObject.location.country}
                  />

                    Keywords

                    Tags:

                    {tags}

                    Specific Topic:

                    {issue}

                    Communication Mode:

                    {communication_mode}

                    Communication with audience:

                    {audience}

                    Decision Method:

                    {decision_method}

                    Face to face, online, or both:

                    {facetoface}

                    Facilitated:

                    {facilitated}

                    Voting:

                    {voting}

                    Number of meeting Days:

                    {numberDays}

                    Related Content

                    Cases
                    Methods
                    Surveys
                    Datasets

                </Col>
                <Col md="8" xs="12" className="main-area">

                      Case

                      {caseObject.title}

                    <ItemGallery items={pics} />

                        First submitted by
                        <Link to={first_author_url}>
                          {first_author_name}
                        </Link>

                        {post_date}

                        Most recent changes by
                        <Link to={last_author_url}>
                          {last_author_name}
                        </Link>

                        {updated_date}

                </Col>
                <Col md="1" className="case-tools hidden-sm-down">

                    <BookmarkToggle
                      thingType="case"
                      thingID={caseObject.id}
                      bookmarked={bookmarked}
                    />

                </Col>
              </Row>
            </Container>
            <Link to={editLinkUrl}>
              <FloatingActionButton className="editButton">
                <ContentPencil />
              </FloatingActionButton>
            </Link>

      );
    } else {
      return
;
    }
  }
}

Case.propTypes = {
  intl: intlShape.isRequired
};

export default connect(mapStateToProps)(injectIntl(Case));
<commit_msg>Hide bookmark icon when not logged in<commit_after>import React from "react";
import { connect } from "react-redux";
import "./Case.css";
import { injectIntl, intlShape } from "react-intl";
import { Link } from "react-router";
import api from "../../utils/api";
import moment from "moment";
import { Container, Row, Col } from "reactstrap";
import CountryMap from "../../components/CountryMap";
import ItemGallery from "./ItemGallery";
import FloatingActionButton from "material-ui/FloatingActionButton";
import ContentPencil from "material-ui/svg-icons/image/edit";
import BookmarkToggle from "../../components/BookmarkToggle";
import caseIconSettings from "../../img/pp-case-icon-settings.svg";
import caseIconFB from "../../img/pp-case-icon-fb.svg";
import caseIconTW from "../../img/pp-case-icon-tw.svg";
import caseIconShare from "../../img/pp-case-icon-share.svg";

class SearchLink extends React.Component {
  render() {
    let tag = this.props.tag;
    let value = this.props.value;
    let locale = this.props.locale;
    if (tag && value && locale) {
      let mapping = {};
      mapping[tag] = value;
      return (
        <Link
          to={{
            pathname: "/" + locale + "/search",
            query: mapping
          }}
        >
          {value}
        </Link>
      );
    } else {
      return
None specified
; // L10N
    }
  }
}

function mapStateToProps({ auth }) {
  const { isAuthenticated } = auth;

  return {
    isAuthenticated
  };
}

export class Case extends React.Component {
  componentWillMount() {
    let component = this;
    let isAuthenticated = this.props.isAuthenticated;
    api.fetchCaseById(this.props.params.nodeID).then(function(the_case) {
      component.setState({
        data: the_case,
        htmlBody: the_case.body,
        isAuthenticated: isAuthenticated
      });
    });
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      isAuthenticated: nextProps.isAuthenticated
    });
  }

  getInnerHTML() {
    let input = "";
    if (this.state && this.state.htmlBody) {
      input = this.state.htmlBody;
    }
    return { __html: input };
  }

  render() {
    if (this.state && this.state.data) {
      const locale = this.props.intl.locale;
      const isAuthenticated = this.props.isAuthenticated;
      let intl = this.props.intl;
      let caseObject = this.state.data;
      let bookmarked = isAuthenticated && caseObject.bookmarked;
      let bookmarkIcon =
;
      if (isAuthenticated) {
        bookmarkIcon = (
          <BookmarkToggle
            thingType="case"
            thingID={caseObject.id}
            bookmarked={bookmarked}
          />
        );
      }
      let issue = caseObject.issue;
      let audience = (
        <SearchLink
          locale={locale}
          tag="communication_with_audience"
          value={caseObject.communication_with_audience}
        />
      );
      let tags =
;
      if (caseObject.tags) {
        tags = caseObject.tags.map(tag => (
          <SearchLink key={tag} locale={locale} tag="tag" value={tag} />
        ));
      }
      let communication_mode = (
        <SearchLink
          locale={locale}
          tag="communication_mode"
          value={caseObject.communication_mode}
        />
      );
      let decision_method = (
        <SearchLink
          locale={locale}
          tag="decision_method"
          value={caseObject.decision_method}
        />
      );
      let facetoface = caseObject.facetoface_online_or_both;
      if (!facetoface) {
        facetoface = "facetoface_not_specified";
      } else {
        facetoface = "facetoface_" + facetoface;
      }
      facetoface = intl.formatMessage({ id: facetoface });
      facetoface = (
        <SearchLink
          locale={locale}
          tag="facetoface_online_or_both"
          value={facetoface}
        />
      );

      let facilitated = intl.formatMessage({
        id: caseObject.facilitated.toLowerCase()
      });
      facilitated = (
        <SearchLink locale={locale} tag="facilitated" value={facilitated} />
      );

      let voting = intl.formatMessage({ id: caseObject.voting });
      voting = <SearchLink locale={locale} tag="voting" value={voting} />;

      let numberDays = caseObject.number_of_meeting_days;
      numberDays = (
        <SearchLink
          locale={locale}
          tag="number_of_meeting_days"
          value={numberDays}
        />
      );

      let post_date = moment(caseObject.post_date).format("LL");
      let updated_date = moment(caseObject.updated_date).format("LL");
      let first_author = caseObject.authors[0];
      let first_author_url = "/" + locale + "/users/" + first_author.user_id;
      let first_author_name = first_author.name;
      let last_author = caseObject.authors.slice(-1)[0];
      let last_author_name = last_author.name;
      let last_author_url = "/" + locale + "/users/" + last_author.user_id;
      let id = this.props.params.nodeID;
      let editLinkUrl = `/${locale}/case/${id}/edit`;
      let awsUrl = process.env.REACT_APP_ASSETS_URL;
      let theLength = "";
      let pics = [];
      if (caseObject && caseObject.lead_image) {
        pics.push(awsUrl + encodeURIComponent(caseObject.lead_image.url));
      }
      if (caseObject && caseObject.other_images.length) {
        theLength = caseObject.other_images;
        Object.keys(theLength).forEach(function(key) {
          let obj = theLength[key];
          pics.push(awsUrl + encodeURIComponent(obj.url));
        });
      }

      return (

            <Container className="detailed-case-component" fluid={true}>
              <Row>
                <Col md="3" className="hidden-sm-down sidepanel hidden-sm-down">
                  <CountryMap
                    city={caseObject.location.city}
                    countrycode={caseObject.location.country}
                  />

                    Keywords

                    Tags:

                    {tags}

                    Specific Topic:

                    {issue}

                    Communication Mode:

                    {communication_mode}

                    Communication with audience:

                    {audience}

                    Decision Method:

                    {decision_method}

                    Face to face, online, or both:

                    {facetoface}

                    Facilitated:

                    {facilitated}

                    Voting:

                    {voting}

                    Number of meeting Days:

                    {numberDays}

                    Related Content

                    Cases
                    Methods
                    Surveys
                    Datasets

                </Col>
                <Col md="8" xs="12" className="main-area">

                      Case

                      {caseObject.title}

                    <ItemGallery items={pics} />

                        First submitted by
                        <Link to={first_author_url}>
                          {first_author_name}
                        </Link>

                        {post_date}

                        Most recent changes by
                        <Link to={last_author_url}>
                          {last_author_name}
                        </Link>

                        {updated_date}

                </Col>
                <Col md="1" className="case-tools hidden-sm-down">

                    {bookmarkIcon}

                </Col>
              </Row>
            </Container>
            <Link to={editLinkUrl}>
              <FloatingActionButton className="editButton">
                <ContentPencil />
              </FloatingActionButton>
            </Link>

      );
    } else {
      return
;
    }
  }
}

Case.propTypes = {
  intl: intlShape.isRequired
};

export default connect(mapStateToProps)(injectIntl(Case));
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Grid, Row, Col, Well, Label } from 'react-bootstrap/lib';
import {connect} from 'react-redux';
import config from '../../config';
import Helmet from 'react-helmet';
import superagent from 'superagent';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';

@connect((store) => {
  return {
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Home extends Component {

  componentDidMount() {
    superagent
    .post('/community')
    .send({ loadStatus: 2 })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if (res.body.status === 1) {
        this.props.dispatch(getBlogEntries(res.body.blogArticles));
      }
    });

    loadArticle = (id) => {
      console.log(id);
      //superagent request with /community/article-id-abc123545
      // you get back here markupdata and all other data from specific article in -> res.

      /* handle this in server.js - in saveto Mongo action:
      function convertToSlug(Text)
      {
          return Text
              .toLowerCase()
              .replace(/[^\w ]+/g,'')
              .replace(/ +/g,'-')
              ;
      }
      save it to aditional field like: urlFriendlyTitel
      also new field: articleId

      then within res. you get urlFriendlyTitel -> add it here to dispatch.push:

      this.props.dispatch.push -> /community/transformed-blog-titel from server.js

      */
    }

  }

  static propTypes = {
    userLoggedIn: PropTypes.string
  }

  render() {
    const styles = require('./Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');
    const { getBlogEntriesState } = this.props;
    // require the logo image both from client and server
    const logoImage = require('./logo.png');

    let blogContentDef = [];
    getBlogEntriesState.articles.forEach(function(entry){
      blogContentDef.push(

 this.loadArticle(1)} className={styles.topLine + ' col-xs-12 ' + styles.hover}>

{entry.userNickname}

{entry.titel}

{entry.category}

{entry.technologies}

{entry.timeFormatted}

      );
    });

    return (

        <Helmet title="Home"/>

{config.app.title}

{config.app.description}

{config.app.subdescription}

React Activity Feed

            {blogContentDef}

    );
  }
}
<commit_msg>asdasd3333122.3<commit_after>import React, { Component, PropTypes } from 'react';
import { Grid, Row, Col, Well, Label } from 'react-bootstrap/lib';
import {connect} from 'react-redux';
import config from '../../config';
import Helmet from 'react-helmet';
import superagent from 'superagent';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';

@connect((store) => {
  return {
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Home extends Component {

  componentDidMount() {
    superagent
    .post('/community')
    .send({ loadStatus: 2 })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if (res.body.status === 1) {
        this.props.dispatch(getBlogEntries(res.body.blogArticles));
      }
    });

    loadArticle = (id) => {
      console.log(id);
      //superagent request with /community/article-id-abc123545
      // you get back here markupdata and all other data from specific article in -> res.

      /* handle this in server.js - in saveto Mongo action:
      function convertToSlug(Text)
      {
          return Text
              .toLowerCase()
              .replace(/[^\w ]+/g,'')
              .replace(/ +/g,'-')
              ;
      }
      save it to aditional field like: urlFriendlyTitel
      also new field: articleId

      then within res. you get urlFriendlyTitel -> add it here to dispatch.push:

      this.props.dispatch.push -> /community/transformed-blog-titel from server.js

      */
    }

  }

  static propTypes = {
    userLoggedIn: PropTypes.string
  }

  render() {
    const styles = require('./Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');
    const { getBlogEntriesState } = this.props;
    // require the logo image both from client and server
    const logoImage = require('./logo.png');

    let blogContentDef = [];
    getBlogEntriesState.articles.forEach(function(entry){
      blogContentDef.push(

 this.loadArticle(1)} className={styles.topLine + ' col-xs-12 ' + styles.hover}>

{entry.userNickname}

{entry.titel}

{entry.category}

{entry.technologies}

{entry.timeFormatted}

      );
    });

    return (

        <Helmet title="Home"/>

{config.app.title}

{config.app.description}

{config.app.subdescription}

React Activity Feed

            {blogContentDef}

    );
  }
}
<|endoftext|>