<commit_before>/**
 * @file TimeList component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import classNames from 'classnames';

import TimeItems from '../_TimeItems';

class TimeList extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            hour: props.hour,
            minute: props.minute,
            second: props.second,
            hoursData: [],
            minutesData: [],
            secondsData: []
        };

    }

    rangeData = (range, minValue, maxValue) => {
        const {isRequired} = this.props;
        let arr = [];
        for (let i = 0; i < range; i++) {
            if (i < 10) {
                i = '0' + i;
            }
            let obj;
            if (isRequired) {
                ((+i > +maxValue) || (+i < +minValue)) ?
                    obj = {text: i, value: false}
                    :
                    obj = {text: i, value: true};
            } else {
                obj = {text: i, value: true};
            }
            arr.push(obj);
        }
        return arr;
    };

    handleHourChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }
        state.hour = value;

        if (minHour == state.hour) {
            state.minutesData = this.rangeData(60, minMinute, 60);
        } else if (maxHour == state.hour) {
            state.minutesData = this.rangeData(60, 0, maxMinute);
        } else if (maxHour == minHour && minHour == state.hour) {
            state.minutesData = this.rangeData(60, minMinute, maxMinute);
        } else {
            state.minutesData = this.rangeData(60);
        }

        if (minHour == state.hour && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, 60);
        } else if (maxHour == state.hour && maxMinute == state.minute) {
            state.secondsData = this.rangeData(60, 0, maxSecond);
        } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, maxSecond);
        } else {
            state.secondsData = this.rangeData(60);
        }
        if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({hour: value, minute: state.minute, second: state.second});
            });
        }
    };

    handleMinuteChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }
        state.minute = value;
        if (minHour == state.hour && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, 60);
        } else if (maxHour == state.hour && maxMinute == state.minute) {
            state.secondsData = this.rangeData(60, 0, maxSecond);
        } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, maxSecond);
        } else {
            state.secondsData = this.rangeData(60);
        }

        if (minValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({hour: state.hour, minute: value, second: state.second});
            });
        }

    };

    handleSecondChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        if (minValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState({
                second: value
            }, () => {
                this.props.onChange && this.props.onChange({hour: state.hour, minute: state.minute, second: value});
            });
        }

    };

    componentDidMount() {
        const {minValue, maxValue} = this.props;
        let state = cloneDeep(this.state);
        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }

        state.hoursData = this.rangeData(24, minHour, maxHour);

        if (minHour == state.hour && minHour != maxHour) {
            state.minutesData = this.rangeData(60, minMinute, 60);
        } else if (maxHour == state.hour && maxHour != minHour) {
            state.minutesData = this.rangeData(60, 0, maxMinute);
        } else if (maxHour == minHour && minHour == state.hour) {
            state.minutesData = this.rangeData(60, minMinute, maxMinute);
        } else {
            state.minutesData = this.rangeData(60);
        }

        if (minHour == state.hour && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, 60);
        } else if (maxHour == state.hour && maxMinute == state.minute) {
            state.secondsData = this.rangeData(60, 0, maxSecond);
        } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, maxSecond);
        } else {
            state.secondsData = this.rangeData(60);
        }

        this.setState(state);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.hour !== this.props.hour || nextProps.minute !== this.props.minute || nextProps.second !== this.props.second ||
            nextProps.minValue !== this.props.minValue || nextProps.maxValue !== this.props.maxValue || this.props.isRequired == nextProps.isRequired) {
            // debugger
            const {minValue, maxValue} = nextProps;
            let state = cloneDeep(this.state);
            let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
            if (minValue) {
                minHour = minValue.split(':')[0];
                minMinute = minValue.split(':')[1];
                minSecond = minValue.split(':')[2];
            }
            if (maxValue) {
                maxHour = maxValue.split(':')[0];
                maxMinute = maxValue.split(':')[1];
                maxSecond = maxValue.split(':')[2];
            }

            state.hoursData = this.rangeData(24, minHour, maxHour);

            if (minHour == state.hour) {
                state.minutesData = this.rangeData(60, minMinute, 60);
            } else if (maxHour == state.hour) {
                state.minutesData = this.rangeData(60, 0, maxMinute);
            } else if (maxHour == minHour && minHour == state.hour) {
                state.minutesData = this.rangeData(60, minMinute, maxMinute);
            } else {
                state.minutesData = this.rangeData(60);
            }

            if (minHour == state.hour && minMinute == state.minute) {
                state.secondsData = this.rangeData(60, minSecond, 60);
            } else if (maxHour == state.hour && maxMinute == state.minute) {
                state.secondsData = this.rangeData(60, 0, maxSecond);
            } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
                state.secondsData = this.rangeData(60, minSecond, maxSecond);
            } else {
                state.secondsData = this.rangeData(60);
            }

            state.hour = nextProps.hour;
            state.minute = nextProps.minute;
            state.second = nextProps.second;
            state.minValue = nextProps.minValue;
            state.maxValue = nextProps.maxValue;
            state.isRequired = nextProps.isRequired;

            this.setState(state);
        }
    }

    render() {

        const {className, popupVisible, dateFormat} = this.props,
            {hour, minute, second, hoursData, minutesData, secondsData} = this.state,

            itemsStyle = {
                width: 100 / (dateFormat.split(':').length) + '%'
            };

        return (
            <div className={classNames('calendar', {
                [className]: className
            })}>

                <TimeItems className="hours"
                           style={itemsStyle}
                           data={hoursData}
                           value={hour}
                           popupVisible={popupVisible}
                           onChange={this.handleHourChange}/>

                {
                    dateFormat.split(':').length > 1 ?
                        <TimeItems className="minutes"
                                   style={itemsStyle}
                                   data={minutesData}
                                   value={minute}
                                   popupVisible={popupVisible}
                                   onChange={this.handleMinuteChange}/>
                        :
                        null
                }

                {
                    dateFormat.split(':').length > 2 ?
                        <TimeItems className="seconds"
                                   style={itemsStyle}
                                   data={secondsData}
                                   value={second}
                                   popupVisible={popupVisible}
                                   onChange={this.handleSecondChange}/>
                        :
                        null
                }

            </div>
        );
    }
}

TimeList.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    popupVisible: PropTypes.bool,
    hour: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    minute: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    second: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    isRequired: PropTypes.bool,
    maxValue: PropTypes.string,
    minValue: PropTypes.string,

    onChange: PropTypes.func

};

export default TimeList;
<commit_msg>update TimeList<commit_after>/**
 * @file TimeList component
 * @author sunday(sunday.wei@derbysoft.com)
 */

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

// Components
import TimeItems from '../_TimeItems';

// Vendors
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import classNames from 'classnames';
import DC from '../_vendors/DateCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class TimeList extends Component {

    static getDerivedStateFromProps(props, state) {

        const minValue = ComponentUtil.getDerivedState(props, state, 'minValue'),
            maxValue = ComponentUtil.getDerivedState(props, state, 'maxValue'),
            isRequired = ComponentUtil.getDerivedState(props, state, 'isRequired');

        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }

        return {
            prevProps: props,
            hoursData: DC.rangeData(24, minHour, maxHour, isRequired),
            minutesData: minHour == state.hour ?
                DC.rangeData(60, minMinute, 60, isRequired)
                :
                maxHour == state.hour ?
                    DC.rangeData(60, 0, maxMinute, isRequired)
                    :
                    maxHour == minHour && minHour == state.hour ?
                        DC.rangeData(60, minMinute, maxMinute, isRequired)
                        :
                        DC.rangeData(60, undefined, undefined, isRequired),
            secondsData: minHour == state.hour && minMinute == state.minute ?
                DC.rangeData(60, minSecond, 60, isRequired)
                :
                maxHour == state.hour && maxMinute == state.minute ?
                    DC.rangeData(60, 0, maxSecond, isRequired)
                    :
                    maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute ?
                        DC.rangeData(60, minSecond, maxSecond, isRequired)
                        :
                        DC.rangeData(60, undefined, undefined, isRequired),
            hour: props.hour,
            minute: props.minute,
            second: props.second,
            minValue: props.minValue,
            maxValue: props.maxValue,
            isRequired
        };

    }

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            hour: props.hour,
            minute: props.minute,
            second: props.second,
            hoursData: [],
            minutesData: [],
            secondsData: []
        };

    }

    rangeData = (range, minValue, maxValue) => {
        const {isRequired} = this.props;
        let arr = [];
        for (let i = 0; i < range; i++) {
            if (i < 10) {
                i = '0' + i;
            }
            let obj;
            if (isRequired) {
                ((+i > +maxValue) || (+i < +minValue)) ?
                    obj = {text: i, value: false}
                    :
                    obj = {text: i, value: true};
            } else {
                obj = {text: i, value: true};
            }
            arr.push(obj);
        }
        return arr;
    };

    /* eslint-disable complexity */
    handleHourChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }
        state.hour = value;

        if (minHour == state.hour) {
            state.minutesData = this.rangeData(60, minMinute, 60);
        } else if (maxHour == state.hour) {
            state.minutesData = this.rangeData(60, 0, maxMinute);
        } else if (maxHour == minHour && minHour == state.hour) {
            state.minutesData = this.rangeData(60, minMinute, maxMinute);
        } else {
            state.minutesData = this.rangeData(60);
        }

        if (minHour == state.hour && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, 60);
        } else if (maxHour == state.hour && maxMinute == state.minute) {
            state.secondsData = this.rangeData(60, 0, maxSecond);
        } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, maxSecond);
        } else {
            state.secondsData = this.rangeData(60);
        }
        if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({hour: value, minute: state.minute, second: state.second});
            });
        }
    };

    handleMinuteChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        let minHour, minMinute, minSecond, maxHour, maxMinute, maxSecond;
        if (minValue) {
            minHour = minValue.split(':')[0];
            minMinute = minValue.split(':')[1];
            minSecond = minValue.split(':')[2];
        }
        if (maxValue) {
            maxHour = maxValue.split(':')[0];
            maxMinute = maxValue.split(':')[1];
            maxSecond = maxValue.split(':')[2];
        }
        state.minute = value;
        if (minHour == state.hour && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, 60);
        } else if (maxHour == state.hour && maxMinute == state.minute) {
            state.secondsData = this.rangeData(60, 0, maxSecond);
        } else if (maxHour == minHour && minHour == state.hour && minMinute == maxMinute && minMinute == state.minute) {
            state.secondsData = this.rangeData(60, minSecond, maxSecond);
        } else {
            state.secondsData = this.rangeData(60);
        }

        if (minValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({hour: state.hour, minute: value, second: state.second});
            });
        }

    };

    handleSecondChange = value => {
        let state = cloneDeep(this.state);
        const {minValue, maxValue} = this.props;
        if (minValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isBefore('2000-01-01 ' + minValue) || maxValue && moment('2000-01-01 ' + state.hour + ':' + state.minute + ':' + value).isAfter('2000-01-01 ' + maxValue)) {
            if (minValue && moment('2000-01-01 ' + value + ':' + state.minute + ':' + state.second).isBefore('2000-01-01 ' + minValue)) {
                state.hour = moment('2000-01-01 ' + minValue).format('HH');
                state.minute = moment('2000-01-01 ' + minValue).format('mm');
                state.second = moment('2000-01-01 ' + minValue).format('ss');
            } else {
                state.hour = moment('2000-01-01 ' + maxValue).format('HH');
                state.minute = moment('2000-01-01 ' + maxValue).format('mm');
                state.second = moment('2000-01-01 ' + maxValue).format('ss');
            }
            this.setState(state, () => {
                this.props.onChange && this.props.onChange({
                    hour: state.hour,
                    minute: state.minute,
                    second: state.second
                });
            });
        } else {
            this.setState({
                second: value
            }, () => {
                this.props.onChange && this.props.onChange({hour: state.hour, minute: state.minute, second: value});
            });
        }

    };

    render() {

        const {className, popupVisible, dateFormat} = this.props,
            {hour, minute, second, hoursData, minutesData, secondsData} = this.state,

            itemsStyle = {
                width: 100 / (dateFormat.split(':').length) + '%'
            };

        return (
            <div className={classNames('calendar', {
                [className]: className
            })}>

                <TimeItems className="hours"
                           style={itemsStyle}
                           data={hoursData}
                           value={hour}
                           popupVisible={popupVisible}
                           onChange={this.handleHourChange}/>

                {
                    dateFormat.split(':').length > 1 ?
                        <TimeItems className="minutes"
                                   style={itemsStyle}
                                   data={minutesData}
                                   value={minute}
                                   popupVisible={popupVisible}
                                   onChange={this.handleMinuteChange}/>
                        :
                        null
                }

                {
                    dateFormat.split(':').length > 2 ?
                        <TimeItems className="seconds"
                                   style={itemsStyle}
                                   data={secondsData}
                                   value={second}
                                   popupVisible={popupVisible}
                                   onChange={this.handleSecondChange}/>
                        :
                        null
                }

            </div>
        );
    }
}

TimeList.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    popupVisible: PropTypes.bool,
    hour: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    minute: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    second: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    isRequired: PropTypes.bool,
    maxValue: PropTypes.string,
    minValue: PropTypes.string,
    dateFormat: PropTypes.string,

    onChange: PropTypes.func

};

export default TimeList;
<|endoftext|>


<commit_before>/*
 * Jumper
 * Handle your models here...
 * -> models
 */

var Model = require('jumper.js').Model,
    utils = require('jumper.js').utils;

var User = Model.extend({

    attributes: {
        first_name: Model.STRING,
        last_name: Model.STRING
    }

});

var Post = Model.extend({

    attributes: {
        author: User,
        title: Model.STRING,
        contents: Model.STRING,
        created: {
            type:Model.DATE,
            default:new Date()
        }
    },

    pre: {
        'save': function(err, model) {
            console.log('calling pre save', model);
            model.slug = utils.slugify(model.title);
        }
    }

});

module.exports.User = User;
module.exports.Post = Post;
<commit_msg>refactor<commit_after>/*
 * Jumper
 * Handle your models here...
 * -> models
 */

var j = require('jumper.js'),
    Model = j.Model,
    utils = j.utils;

var User = Model.extend({

    attributes: {
        first_name: Model.STRING,
        last_name: Model.STRING
    }

});

var Post = Model.extend({

    attributes: {
        author: User,
        title: Model.STRING,
        contents: Model.STRING,
        created: {
            type:Model.DATE,
            default:new Date()
        }
    },

    pre: {
        'save': function(err, model) {
            console.log('calling pre save', model);
            model.slug = utils.slugify(model.title);
        }
    }

});

module.exports.User = User;
module.exports.Post = Post;
<|endoftext|>


<commit_before>import queryString from "query-string";
import { createStateFromQueryOrJSONs } from "./recomputeReduxState";
import { PAGE_CHANGE, URL_QUERY_CHANGE_WITH_COMPUTED_STATE } from "./types";

/* Given a URL, what "page" should be displayed?
 * "page" means the main app, splash page, status page etc
 * If in doubt, we go to the datasetLoader page as this will
 * redirect to the splash page if the datasets are unavailable
 */
export const chooseDisplayComponentFromURL = (url) => {
  const parts = url.toLowerCase().replace(/^\/+/, "").replace(/\/+$/, "").split("/");
  if (
    !parts.length ||
    (parts.length === 1 && parts[0] === "") ||
    (parts.length === 1 && parts[0] === "staging") ||
    (parts.length === 1 && parts[0] === "community") ||
    (parts.length === 1 && parts[0] === "narratives") ||
    (parts.length === 2 && parts[0] === "groups")
  ) {
    return "splash";
  } else if (parts[0] === "status") {
    return "status";
  }
  return "datasetLoader"; // fallthrough
};

/* changes the state of the page and (perhaps) the dataset displayed.
This function is used throughout the app for all navigation to another page, (including braowserBackForward - see function below)
The exception is for navigation requests that specify only the query changes, or that have an identical pathname to that selected.
Note that this function is not pure, in that it may change the URL

ARGUMENTS:
(1) path - REQUIRED - the destination path - e.g. "zika" or "flu/..." (does not include query)
(2) query - OPTIONAL (default: undefined) - see below
(3) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)

UNDERSTANDING QUERY (SLIGHTLY CONFUSING)
This function changes the pathname (stored in the datasets reducer) and modifies the URL pathname and query
accordingly in the middleware. But the URL query is not processed further.
Because the datasets reducer has changed, the <App> (or whichever display component we're on) will update.
In <App>, this causes a call to loadJSONs, which will, as part of it's dispatch, use the URL state of query.
In this way, the URL query is "used".
*/
export const changePage = ({
  path = undefined,
  query = undefined,
  queryToDisplay = undefined, /* doesn't affect state, only URL. defaults to query unless specified */
  push = true,
  changeDataset = true
} = {}) => (dispatch, getState) => {
  const oldState = getState();
  // console.warn("CHANGE PAGE!", path, query, queryToDisplay, push);

  /* set some defaults */
  if (!path) path = window.location.pathname;  // eslint-disable-line
  if (!query) query = queryString.parse(window.location.search);  // eslint-disable-line
  if (!queryToDisplay) queryToDisplay = query; // eslint-disable-line
  /* some booleans */
  const pathHasChanged = oldState.general.pathname !== path;

  if (changeDataset || pathHasChanged) {
    dispatch({
      type: PAGE_CHANGE,
      path,
      displayComponent: chooseDisplayComponentFromURL(path),
      pushState: push,
      query
    });
    return;
  }

  /* the path (dataset) remains the same... but the state may be modulated by the query */
  const newState = createStateFromQueryOrJSONs({oldState, query});
  dispatch({
    type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
    ...newState,
    pushState: push,
    query: queryToDisplay
  });
};

/* a 404 uses the same machinery as changePage, but it's not a thunk.
 * Note that a 404 maintains the "bad" URL -- see https://github.com/nextstrain/auspice/issues/700
 */
export const goTo404 = (errorMessage) => ({
  type: PAGE_CHANGE,
  displayComponent: "splash",
  errorMessage,
  pushState: true
});

export const EXPERIMENTAL_showMainDisplayMarkdown = ({query, queryToDisplay}) =>
  (dispatch, getState) => {
    const newState = createStateFromQueryOrJSONs({oldState: getState(), query});
    newState.controls.panelsToDisplay = ["EXPERIMENTAL_MainDisplayMarkdown"];
    dispatch({
      type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
      ...newState,
      pushState: true,
      query: queryToDisplay
    });
  };
<commit_msg>bugfix: ensure `dispatch` available for `createStateFromQueryOrJSONs`<commit_after>import queryString from "query-string";
import { createStateFromQueryOrJSONs } from "./recomputeReduxState";
import { PAGE_CHANGE, URL_QUERY_CHANGE_WITH_COMPUTED_STATE } from "./types";

/* Given a URL, what "page" should be displayed?
 * "page" means the main app, splash page, status page etc
 * If in doubt, we go to the datasetLoader page as this will
 * redirect to the splash page if the datasets are unavailable
 */
export const chooseDisplayComponentFromURL = (url) => {
  const parts = url.toLowerCase().replace(/^\/+/, "").replace(/\/+$/, "").split("/");
  if (
    !parts.length ||
    (parts.length === 1 && parts[0] === "") ||
    (parts.length === 1 && parts[0] === "staging") ||
    (parts.length === 1 && parts[0] === "community") ||
    (parts.length === 1 && parts[0] === "narratives") ||
    (parts.length === 2 && parts[0] === "groups")
  ) {
    return "splash";
  } else if (parts[0] === "status") {
    return "status";
  }
  return "datasetLoader"; // fallthrough
};

/* changes the state of the page and (perhaps) the dataset displayed.
This function is used throughout the app for all navigation to another page, (including braowserBackForward - see function below)
The exception is for navigation requests that specify only the query changes, or that have an identical pathname to that selected.
Note that this function is not pure, in that it may change the URL

ARGUMENTS:
(1) path - REQUIRED - the destination path - e.g. "zika" or "flu/..." (does not include query)
(2) query - OPTIONAL (default: undefined) - see below
(3) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)

UNDERSTANDING QUERY (SLIGHTLY CONFUSING)
This function changes the pathname (stored in the datasets reducer) and modifies the URL pathname and query
accordingly in the middleware. But the URL query is not processed further.
Because the datasets reducer has changed, the <App> (or whichever display component we're on) will update.
In <App>, this causes a call to loadJSONs, which will, as part of it's dispatch, use the URL state of query.
In this way, the URL query is "used".
*/
export const changePage = ({
  path = undefined,
  query = undefined,
  queryToDisplay = undefined, /* doesn't affect state, only URL. defaults to query unless specified */
  push = true,
  changeDataset = true
} = {}) => (dispatch, getState) => {
  const oldState = getState();
  // console.warn("CHANGE PAGE!", path, query, queryToDisplay, push);

  /* set some defaults */
  if (!path) path = window.location.pathname;  // eslint-disable-line
  if (!query) query = queryString.parse(window.location.search);  // eslint-disable-line
  if (!queryToDisplay) queryToDisplay = query; // eslint-disable-line
  /* some booleans */
  const pathHasChanged = oldState.general.pathname !== path;

  if (changeDataset || pathHasChanged) {
    dispatch({
      type: PAGE_CHANGE,
      path,
      displayComponent: chooseDisplayComponentFromURL(path),
      pushState: push,
      query
    });
    return;
  }

  /* the path (dataset) remains the same... but the state may be modulated by the query */
  const newState = createStateFromQueryOrJSONs({oldState, query, dispatch});
  dispatch({
    type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
    ...newState,
    pushState: push,
    query: queryToDisplay
  });
};

/* a 404 uses the same machinery as changePage, but it's not a thunk.
 * Note that a 404 maintains the "bad" URL -- see https://github.com/nextstrain/auspice/issues/700
 */
export const goTo404 = (errorMessage) => ({
  type: PAGE_CHANGE,
  displayComponent: "splash",
  errorMessage,
  pushState: true
});

export const EXPERIMENTAL_showMainDisplayMarkdown = ({query, queryToDisplay}) =>
  (dispatch, getState) => {
    const newState = createStateFromQueryOrJSONs({oldState: getState(), query, dispatch});
    newState.controls.panelsToDisplay = ["EXPERIMENTAL_MainDisplayMarkdown"];
    dispatch({
      type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
      ...newState,
      pushState: true,
      query: queryToDisplay
    });
  };
<|endoftext|>


<commit_before>
import React, {PureComponent, PropTypes} from 'react';
import last from 'lodash/last';
import Emitter from 'tiny-emitter';

import {withChart} from './../../hocs/withHighcharts';

const emitter = new Emitter();

class ColumnWidget extends PureComponent {

  constructor(props) {
    super(props);
    this.el = null;

    emitter.on('receive_onPointUpdate', this.receiveOnPointUpdate.bind(this));

    const {widget} = props;

    const chartOptions = {
      xAxis: {
        categories: widget.series.map(s => s.data)[0].map(item => item.description)
      },
      series:  widget.series.map(s => {
        return {
          name: s.name,
          label: s.data.map(item => item.description),
          data: s.data.map(item => item.value)
        }
      }),
      title: {
        text: null
      },
      chart: {
        marginBottom: 100,
        type: 'column'
      },
      plotOptions: {
        series: {
          animation: false,
          point: {
            events: {
              mouseOver: this.onPointUpdate
            }
          },
          // allowPointSelect: true,
          states: {
            hover: {
              color: null,
              borderWidth:5,
              borderColor:'Blue'
            }
          }
        },
      },
      legend: {
        enabled: false,
      },
      credits: {
        enabled: false
      },
      tooltip: {
        followTouchMove: true
      }
    };

    const restOptions = {
      fauxLegend: widget.series.map(s => {
        const item = last(s.data);
        return {
          seriesName: s.name,
          label: item.description,
          value: item.value
        }
      })
    };

    this.state = {
      chartOptions,
      ...restOptions
    };
  }

  // this is the scope of point
  onPointUpdate(e) {
    this.select();
    // console.log(this.category, this.y, this.series.name);
    emitter.emit('receive_onPointUpdate', {
      seriesName: this.series.name,
      label: this.category,
      value: this.y,
    });
  }

  receiveOnPointUpdate(options, cb) {
    const {label, value, seriesName} = options;
    console.log(label, value, seriesName);

    const nextFauxLegend = this.state.fauxLegend.map(f => {
      if (f.seriesName === seriesName) {
        f.label = label;
        f.value = value;
      }
      return f;
    });
    this.setState({fauxLegend: nextFauxLegend});
  }

  componentDidMount() {
    if (this.el === null) {
      throw new Error('must provide a container element');
    }
    const _options = this.state.chartOptions;
    _options.chart.renderTo = this.el;
    this.instance = this.props.create(_options);

    // select the last column
    const lastIdx = this.instance.series[0].data.length - 1;
    this.instance.series[0].data[lastIdx].select();
  }

  // todo
  // componentWillUpdate(nextProps, nextState) {
  //   this.props.update(nextState.chartOptions);
  // }

  render() {
    const chartType = this.state.chartOptions.chart.type;
    const dateUpdated = this.props.widget.dateUpdated;
    const datetimeUpdate = new Date(dateUpdated).toJSON();
    const title = this.props.widget.title;
    const {fauxLegend} = this.state;

    console.log(...fauxLegend)

    return (
      <article className={`chart--${chartType}`} role="article">

{title}

          Last updated <time dateTime={datetimeUpdate}>{dateUpdated}</time>
          What is this?

          {fauxLegend.length &&
<time>{fauxLegend[0].label}</time>
}

 this.el = el} />
          {fauxLegend.length &&

                {fauxLegend.map((legend, idx) => (

                      <svg width="12" height="12">
                        <g className="legend--icon">
                          {legend.color && <rect x="0" y="0" width="12" height="12" fill={legend.color} visibility="visible" rx="6" ry="6"></rect>}
                        </g>
                      </svg>
                      {legend.seriesName}

                    {legend.value}

                ))}

}

    );
  }

}

ColumnWidget.propTypes = {
  // a column chart must have time-series data
};

export default withChart(ColumnWidget);

<commit_msg>fix(remove unneeded tooltip): (#9)<commit_after>
import React, {PureComponent, PropTypes} from 'react';
import last from 'lodash/last';
import Emitter from 'tiny-emitter';

import {withChart} from './../../hocs/withHighcharts';

const emitter = new Emitter();

class ColumnWidget extends PureComponent {

  constructor(props) {
    super(props);
    this.el = null;

    emitter.on('receive_onPointUpdate', this.receiveOnPointUpdate.bind(this));

    const {widget} = props;

    const chartOptions = {
      xAxis: {
        categories: widget.series.map(s => s.data)[0].map(item => item.description)
      },
      series:  widget.series.map(s => {
        return {
          name: s.name,
          label: s.data.map(item => item.description),
          data: s.data.map(item => item.value)
        }
      }),
      title: {
        text: null
      },
      chart: {
        marginBottom: 100,
        type: 'column'
      },
      plotOptions: {
        series: {
          animation: false,
          point: {
            events: {
              mouseOver: this.onPointUpdate
            }
          },
          // allowPointSelect: true,
          states: {
            hover: {
              color: null,
              borderWidth:5,
              borderColor:'Blue'
            }
          }
        },
      },
      legend: {
        enabled: false,
      },
      credits: {
        enabled: false
      },
      tooltip: {
        enabled: false,
        // followTouchMove: true
      }
    };

    const restOptions = {
      fauxLegend: widget.series.map(s => {
        const item = last(s.data);
        return {
          seriesName: s.name,
          label: item.description,
          value: item.value
        }
      })
    };

    this.state = {
      chartOptions,
      ...restOptions
    };
  }

  // this is the scope of point
  onPointUpdate(e) {
    // console.log(this.category, this.y, this.series.name);

    this.select();
    emitter.emit('receive_onPointUpdate', {
      seriesName: this.series.name,
      label: this.category,
      value: this.y,
    });
  }

  receiveOnPointUpdate(options, cb) {
    // console.log(label, value, seriesName);

    const {label, value, seriesName} = options;
    const nextFauxLegend = this.state.fauxLegend.map(f => {
      if (f.seriesName === seriesName) {
        f.label = label;
        f.value = value;
      }
      return f;
    });
    this.setState({fauxLegend: nextFauxLegend});
  }

  componentDidMount() {
    if (this.el === null) {
      throw new Error('must provide a container element');
    }
    const _options = this.state.chartOptions;
    _options.chart.renderTo = this.el;
    this.instance = this.props.create(_options);

    // select the last column
    const lastIdx = this.instance.series[0].data.length - 1;
    this.instance.series[0].data[lastIdx].select();
  }

  // todo
  // componentWillUpdate(nextProps, nextState) {
  //   this.props.update(nextState.chartOptions);
  // }

  render() {
    const chartType = this.state.chartOptions.chart.type;
    const dateUpdated = this.props.widget.dateUpdated;
    const datetimeUpdate = new Date(dateUpdated).toJSON();
    const title = this.props.widget.title;
    const {fauxLegend} = this.state;

    console.log(...fauxLegend)

    return (
      <article className={`chart--${chartType}`} role="article">

{title}

          Last updated <time dateTime={datetimeUpdate}>{dateUpdated}</time>
          What is this?

          {fauxLegend.length &&
<time>{fauxLegend[0].label}</time>
}

 this.el = el} />
          {fauxLegend.length &&

                {fauxLegend.map((legend, idx) => (

                      <svg width="12" height="12">
                        <g className="legend--icon">
                          {legend.color && <rect x="0" y="0" width="12" height="12" fill={legend.color} visibility="visible" rx="6" ry="6"></rect>}
                        </g>
                      </svg>
                      {legend.seriesName}

                    {legend.value}

                ))}

}

    );
  }

}

ColumnWidget.propTypes = {
  // a column chart must have time-series data
};

export default withChart(ColumnWidget);

<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

import InputBase from './InputBase';
import Box, { omitBoxProps, pickBoxProps } from '../box';
import ValidationText from '../validationText';

import theme from './theme.css';

class SingleLineInputBase extends PureComponent {
  state = {
    inputHasfocus: false,
  };

  handleBlur = event => {
    this.setState({ inputHasfocus: false });
    if (this.props.onBlur) {
      this.props.onBlur(event);
    }
  };

  handleFocus = event => {
    this.setState({ inputHasfocus: true });
    if (this.props.onFocus) {
      this.props.onFocus(event);
    }
  };

  renderOneOrMultipleElements(prop) {
    if (Array.isArray(prop)) {
      return prop.map((element, index) => React.cloneElement(element, { key: index }));
    }

    return prop;
  }

  render() {
    const {
      className,
      connectedLeft,
      connectedRight,
      disabled,
      error,
      helpText,
      onFocus,
      onBlur,
      prefix,
      inverse,
      readOnly,
      suffix,
      width,
      ...others
    } = this.props;

    const classNames = cx(
      theme['wrapper'],
      {
        [theme['has-focus']]: this.state.inputHasfocus,
        [theme['has-error']]: error,
        [theme['has-connected-left']]: connectedLeft,
        [theme['has-connected-right']]: connectedRight,
        [theme['is-disabled']]: disabled,
        [theme['is-inverse']]: inverse,
        [theme['is-read-only']]: readOnly,
      },
      className,
    );

    const boxProps = pickBoxProps(others);
    const inputProps = {
      disabled,
      error,
      inverse,
      onBlur: this.handleBlur,
      onFocus: this.handleFocus,
      readOnly,
      ...omitBoxProps(others),
    };

    return (
      <Box className={classNames} {...boxProps}>

          {connectedLeft}
          <div
            className={theme['input-inner-wrapper']}
            style={{ width: width && `${width}px`, flex: width && '0 0 auto' }}
          >
            {prefix &&
{this.renderOneOrMultipleElements(prefix)}
}
            <InputBase {...inputProps} />
            {suffix &&
{this.renderOneOrMultipleElements(suffix)}
}

          {connectedRight}

        <ValidationText error={error} help={helpText} inverse={inverse} />
      </Box>
    );
  }
}

SingleLineInputBase.propTypes = {
  /** Element stuck to the left hand side of the component. */
  connectedLeft: PropTypes.element,
  /** Element stuck to the right hand side of the component. */
  connectedRight: PropTypes.element,
  /** The text string/element to use as a prefix inside the input field */
  prefix: PropTypes.oneOfType([PropTypes.array, PropTypes.element]),
  /** The text string/element to use as a suffix inside the input field */
  suffix: PropTypes.oneOfType([PropTypes.array, PropTypes.element]),
  /** A custom width (in pixels) for the input field */
  width: PropTypes.number,
};

export default SingleLineInputBase;
<commit_msg>Don't force the width to be in pixels, it can be any value + unit<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

import InputBase from './InputBase';
import Box, { omitBoxProps, pickBoxProps } from '../box';
import ValidationText from '../validationText';

import theme from './theme.css';

class SingleLineInputBase extends PureComponent {
  state = {
    inputHasfocus: false,
  };

  handleBlur = event => {
    this.setState({ inputHasfocus: false });
    if (this.props.onBlur) {
      this.props.onBlur(event);
    }
  };

  handleFocus = event => {
    this.setState({ inputHasfocus: true });
    if (this.props.onFocus) {
      this.props.onFocus(event);
    }
  };

  renderOneOrMultipleElements(prop) {
    if (Array.isArray(prop)) {
      return prop.map((element, index) => React.cloneElement(element, { key: index }));
    }

    return prop;
  }

  render() {
    const {
      className,
      connectedLeft,
      connectedRight,
      disabled,
      error,
      helpText,
      onFocus,
      onBlur,
      prefix,
      inverse,
      readOnly,
      suffix,
      width,
      ...others
    } = this.props;

    const classNames = cx(
      theme['wrapper'],
      {
        [theme['has-focus']]: this.state.inputHasfocus,
        [theme['has-error']]: error,
        [theme['has-connected-left']]: connectedLeft,
        [theme['has-connected-right']]: connectedRight,
        [theme['is-disabled']]: disabled,
        [theme['is-inverse']]: inverse,
        [theme['is-read-only']]: readOnly,
      },
      className,
    );

    const boxProps = pickBoxProps(others);
    const inputProps = {
      disabled,
      error,
      inverse,
      onBlur: this.handleBlur,
      onFocus: this.handleFocus,
      readOnly,
      ...omitBoxProps(others),
    };

    return (
      <Box className={classNames} {...boxProps}>

          {connectedLeft}

            {prefix &&
{this.renderOneOrMultipleElements(prefix)}
}
            <InputBase {...inputProps} />
            {suffix &&
{this.renderOneOrMultipleElements(suffix)}
}

          {connectedRight}

        <ValidationText error={error} help={helpText} inverse={inverse} />
      </Box>
    );
  }
}

SingleLineInputBase.propTypes = {
  /** Element stuck to the left hand side of the component. */
  connectedLeft: PropTypes.element,
  /** Element stuck to the right hand side of the component. */
  connectedRight: PropTypes.element,
  /** The text string/element to use as a prefix inside the input field */
  prefix: PropTypes.oneOfType([PropTypes.array, PropTypes.element]),
  /** The text string/element to use as a suffix inside the input field */
  suffix: PropTypes.oneOfType([PropTypes.array, PropTypes.element]),
  /** A custom width for the input field */
  width: PropTypes.string,
};

export default SingleLineInputBase;
<|endoftext|>


<commit_before>// Main function
requirejs(['./app', './bridge', './console_api', 'jquery'], function (app, Bridge, API, $) {
    // 1. Load the app and UI (done)

    // 2. Publish 'Bridge' interface that the Ruby side will need to return from callbacks.
    // window.Bridge = Bridge; // This is now done with requirejs('bridge')

    // 3. Publish 'Console' API interface on which the consoles on the Ruby side will call functions.
    window.Console = API;

    // 4. Fallback for svg-resources to png images (this was for old Internet Explorer versions).
    $('img').on('error', function () {
        var img = $(this),
            src = img.attr('src');
        img.attr('src', src.replace(/\.svg$/, '.png'));
    });

    // 5. Catch uncaught errors in the WebDialog and send them to the console.
    // There are some errors by the ACE editor (when double-clicking or selection
    // that goes outside the editor) that would be silent in a normal Internet Explorer
    // but cause popups in SketchUp.
    window.onerror = function(messageOrEvent, source, lineNumber, columnNumber, errorObject) {
        window.console.log([messageOrEvent, source, lineNumber, columnNumber, errorObject]);
        if (!errorObject) {
            errorObject = new Error();
            errorObject.name = 'Error';
            errorObject.message = messageOrEvent;
            errorObject.fileName = source;
            errorObject.lineNumber = lineNumber;
            errorObject.columnNumber = columnNumber;
        }
        API.javaScriptError(errorObject);
        return true;
    };
});
<commit_msg>Fix SVG being rendered too big in WebDialog/MSIE<commit_after>// Main function
requirejs(['./app', './bridge', './console_api', 'jquery'], function (app, Bridge, API, $) {
    // 1. Load the app and UI (done)

    // 2. Publish 'Bridge' interface that the Ruby side will need to return from callbacks.
    // window.Bridge = Bridge; // This is now done with requirejs('bridge')

    // 3. Publish 'Console' API interface on which the consoles on the Ruby side will call functions.
    window.Console = API;

    // 4. Fallback for svg-resources to png images.
    if (navigator.userAgent.match(/MSIE/i)) {
        // Internet Explorer (even if it can render svg, it does not scale them properly).
        $('img').each(function (index, img) {
            img.src = img.src.replace(/\.svg$/, '.png');
        });
    } else {
        // Fallback on load failure.
        $('img').on('error', function () {
            var img = $(this),
                src = img.attr('src');
            img.attr('src', src.replace(/\.svg$/, '.png'));
        });
    }

    // 5. Catch uncaught errors in the WebDialog and send them to the console.
    // There are some errors by the ACE editor (when double-clicking or selection
    // that goes outside the editor) that would be silent in a normal Internet Explorer
    // but cause popups in SketchUp.
    window.onerror = function(messageOrEvent, source, lineNumber, columnNumber, errorObject) {
        window.console.log([messageOrEvent, source, lineNumber, columnNumber, errorObject]);
        if (!errorObject) {
            errorObject = new Error();
            errorObject.name = 'Error';
            errorObject.message = messageOrEvent;
            errorObject.fileName = source;
            errorObject.lineNumber = lineNumber;
            errorObject.columnNumber = columnNumber;
        }
        API.javaScriptError(errorObject);
        return true;
    };
});
<|endoftext|>


<commit_before>jsio('import lib.Enum as Enum');

exports.interpolate = function(a, b, x) { return a * (1 - x) + b * x; }

exports.random = function(a, b, rand) { return a + ((rand || Math.random)() * (b - a) | 0); }

exports.randomInclusive = function(a, b, rand) { return exports.random(a, b+1, rand); }
exports.rand = Math.random;
//FIXME integer is a reserved word XXX
exports.integer = exports.truncate = function(a) { return a | 0; }

exports.clip = function(num, min, max) { return Math.max(Math.min(num, max), min); }

var round = exports.round = function(a, precision, method) {
	if (!method || method == round.ROUND_HALF_AWAY_FROM_ZERO) {
		return a.toFixed(precision);
	}

	if(!precision) {
		if (method == round.ROUND_HALF_UP) { Math.round(a); }

		//FIXME integer is a reserved word XXX
		var integer = a | 0,
			frac = a - integer
			half = frac == 0.5 || frac == -0.5;
		if (!half) { return Math.round(a); }

		var sign = a < 0 ? -1 : 1;
		switch(method) {
			case round.ROUND_HALF_TO_EVEN:
				return integer % 2 ? integer + sign : integer
			case round.ROUND_HALF_TO_ODD:
				return integer % 2 ? integer : integer + sign;
			case round.ROUND_HALF_STOCHASTIC:
				return Math.random() < 0.5 ? integer + sign : integer
			case round.ROUND_HALF_ALTERNATE:
				return (round.alt = !round.alt) ? integer + sign : integer
		}
	}

	var integer = a | 0,
		frac = a - integer
		p = Math.pow(10, precision);
	return (integer + round(frac * p, 0, method) / p).toFixed(precision);
}

round.alt = true;

Enum.call(round, 'ROUND_HALF_UP', 'ROUND_HALF_AWAY_FROM_ZERO', 'ROUND_HALF_TO_EVEN', 'ROUND_HALF_STOCHASTIC', 'ROUND_HALF_ALTERNATE');
<commit_msg>Added math.util function to get the sign of a number<commit_after>jsio('import lib.Enum as Enum');

exports.interpolate = function(a, b, x) { return a * (1 - x) + b * x; }

exports.random = function(a, b, rand) { return a + ((rand || Math.random)() * (b - a) | 0); }

exports.randomInclusive = function(a, b, rand) { return exports.random(a, b+1, rand); }
exports.rand = Math.random;
//FIXME integer is a reserved word XXX
exports.integer = exports.truncate = function(a) { return a | 0; }

exports.clip = function(num, min, max) { return Math.max(Math.min(num, max), min); }

exports.sign = function (num) {
	return num && num / Math.abs(num);
};

var round = exports.round = function(a, precision, method) {
	if (!method || method == round.ROUND_HALF_AWAY_FROM_ZERO) {
		return a.toFixed(precision);
	}

	if(!precision) {
		if (method == round.ROUND_HALF_UP) { Math.round(a); }

		//FIXME integer is a reserved word XXX
		var integer = a | 0,
			frac = a - integer
			half = frac == 0.5 || frac == -0.5;
		if (!half) { return Math.round(a); }

		var sign = a < 0 ? -1 : 1;
		switch(method) {
			case round.ROUND_HALF_TO_EVEN:
				return integer % 2 ? integer + sign : integer
			case round.ROUND_HALF_TO_ODD:
				return integer % 2 ? integer : integer + sign;
			case round.ROUND_HALF_STOCHASTIC:
				return Math.random() < 0.5 ? integer + sign : integer
			case round.ROUND_HALF_ALTERNATE:
				return (round.alt = !round.alt) ? integer + sign : integer
		}
	}

	var integer = a | 0,
		frac = a - integer
		p = Math.pow(10, precision);
	return (integer + round(frac * p, 0, method) / p).toFixed(precision);
}

round.alt = true;

Enum.call(round, 'ROUND_HALF_UP', 'ROUND_HALF_AWAY_FROM_ZERO', 'ROUND_HALF_TO_EVEN', 'ROUND_HALF_STOCHASTIC', 'ROUND_HALF_ALTERNATE');
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

'use strict';

const classNames = require('classnames');
const React = require('react');
const ReactDOM = require('react-dom');
import PropTypes from 'prop-types';

// Shamelessly ripped off Modal.js.  There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
// pass in a custom control as the actual body.

module.exports = {
    ContextualMenuContainerId: "mx_ContextualMenu_Container",

    propTypes: {
        menuWidth: PropTypes.number,
        menuHeight: PropTypes.number,
        chevronOffset: PropTypes.number,
        menuColour: PropTypes.string,
        chevronFace: PropTypes.string, // top, bottom, left, right
    },

    getOrCreateContainer: function() {
        let container = document.getElementById(this.ContextualMenuContainerId);

        if (!container) {
            container = document.createElement("div");
            container.id = this.ContextualMenuContainerId;
            document.body.appendChild(container);
        }

        return container;
    },

    createMenu: function(Element, props) {
        const self = this;

        const closeMenu = function(...args) {
            ReactDOM.unmountComponentAtNode(self.getOrCreateContainer());

            if (props && props.onFinished) {
                props.onFinished.apply(null, args);
            }
        };

        // Close the menu on window resize
        const windowResize = function() {
            closeMenu();
        };

        const position = {};
        let chevronFace = null;

        if (props.top) {
            position.top = props.top;
        } else {
            position.bottom = props.bottom;
        }

        if (props.left) {
            position.left = props.left;
            chevronFace = 'left';
        } else {
            position.right = props.right;
            chevronFace = 'right';
        }

        const chevronOffset = {};
        if (props.chevronFace) {
            chevronFace = props.chevronFace;
        }
        if (chevronFace === 'top' || chevronFace === 'bottom') {
            chevronOffset.left = props.chevronOffset;
        } else {
            chevronOffset.top = props.chevronOffset;
        }

        // To override the default chevron colour, if it's been set
        let chevronCSS = "";
        if (props.menuColour) {
            chevronCSS = `
                .mx_ContextualMenu_chevron_left:after {
                    border-right-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_right:after {
                    border-left-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_top:after {
                    border-left-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_bottom:after {
                    border-left-color: ${props.menuColour};
                }
            `;
        }

        const chevron =

;
        const className = 'mx_ContextualMenu_wrapper';

        const menuClasses = classNames({
            'mx_ContextualMenu': true,
            'mx_ContextualMenu_left': chevronFace === 'left',
            'mx_ContextualMenu_right': chevronFace === 'right',
            'mx_ContextualMenu_top': chevronFace === 'top',
            'mx_ContextualMenu_bottom': chevronFace === 'bottom',
        });

        const menuStyle = {};
        if (props.menuWidth) {
            menuStyle.width = props.menuWidth;
        }

        if (props.menuHeight) {
            menuStyle.height = props.menuHeight;
        }

        if (props.menuColour) {
            menuStyle["backgroundColor"] = props.menuColour;
        }

        if (!isNaN(Number(props.menuPaddingTop))) {
            menuStyle["paddingTop"] = props.menuPaddingTop;
        }
        if (!isNaN(Number(props.menuPaddingLeft))) {
            menuStyle["paddingLeft"] = props.menuPaddingLeft;
        }
        if (!isNaN(Number(props.menuPaddingBottom))) {
            menuStyle["paddingBottom"] = props.menuPaddingBottom;
        }
        if (!isNaN(Number(props.menuPaddingRight))) {
            menuStyle["paddingRight"] = props.menuPaddingRight;
        }

        // FIXME: If a menu uses getDefaultProps it clobbers the onFinished
        // property set here so you can't close the menu from a button click!
        const menu = (

                    { chevron }
                    <Element {...props} onFinished={closeMenu} onResize={windowResize} />

        );

        ReactDOM.render(menu, this.getOrCreateContainer());

        return {close: closeMenu};
    },
};
<commit_msg>Add missing PropType declarations.<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

'use strict';

const classNames = require('classnames');
const React = require('react');
const ReactDOM = require('react-dom');
import PropTypes from 'prop-types';

// Shamelessly ripped off Modal.js.  There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
// pass in a custom control as the actual body.

module.exports = {
    ContextualMenuContainerId: "mx_ContextualMenu_Container",

    propTypes: {
        top: PropTypes.number,
        bottom: PropTypes.number,
        left: PropTypes.number,
        right: PropTypes.number,
        menuWidth: PropTypes.number,
        menuHeight: PropTypes.number,
        chevronOffset: PropTypes.number,
        menuColour: PropTypes.string,
        chevronFace: PropTypes.string, // top, bottom, left, right
        // Function to be called on menu close
        onFinished: PropTypes.func,
        menuPaddingTop: PropTypes.number,
        menuPaddingRight: PropTypes.number,
        menuPaddingBottom: PropTypes.number,
        menuPaddingLeft: PropTypes.number,
    },

    getOrCreateContainer: function() {
        let container = document.getElementById(this.ContextualMenuContainerId);

        if (!container) {
            container = document.createElement("div");
            container.id = this.ContextualMenuContainerId;
            document.body.appendChild(container);
        }

        return container;
    },

    createMenu: function(Element, props) {
        const self = this;

        const closeMenu = function(...args) {
            ReactDOM.unmountComponentAtNode(self.getOrCreateContainer());

            if (props && props.onFinished) {
                props.onFinished.apply(null, args);
            }
        };

        // Close the menu on window resize
        const windowResize = function() {
            closeMenu();
        };

        const position = {};
        let chevronFace = null;

        if (props.top) {
            position.top = props.top;
        } else {
            position.bottom = props.bottom;
        }

        if (props.left) {
            position.left = props.left;
            chevronFace = 'left';
        } else {
            position.right = props.right;
            chevronFace = 'right';
        }

        const chevronOffset = {};
        if (props.chevronFace) {
            chevronFace = props.chevronFace;
        }
        if (chevronFace === 'top' || chevronFace === 'bottom') {
            chevronOffset.left = props.chevronOffset;
        } else {
            chevronOffset.top = props.chevronOffset;
        }

        // To override the default chevron colour, if it's been set
        let chevronCSS = "";
        if (props.menuColour) {
            chevronCSS = `
                .mx_ContextualMenu_chevron_left:after {
                    border-right-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_right:after {
                    border-left-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_top:after {
                    border-left-color: ${props.menuColour};
                }
                .mx_ContextualMenu_chevron_bottom:after {
                    border-left-color: ${props.menuColour};
                }
            `;
        }

        const chevron =

;
        const className = 'mx_ContextualMenu_wrapper';

        const menuClasses = classNames({
            'mx_ContextualMenu': true,
            'mx_ContextualMenu_left': chevronFace === 'left',
            'mx_ContextualMenu_right': chevronFace === 'right',
            'mx_ContextualMenu_top': chevronFace === 'top',
            'mx_ContextualMenu_bottom': chevronFace === 'bottom',
        });

        const menuStyle = {};
        if (props.menuWidth) {
            menuStyle.width = props.menuWidth;
        }

        if (props.menuHeight) {
            menuStyle.height = props.menuHeight;
        }

        if (props.menuColour) {
            menuStyle["backgroundColor"] = props.menuColour;
        }

        if (!isNaN(Number(props.menuPaddingTop))) {
            menuStyle["paddingTop"] = props.menuPaddingTop;
        }
        if (!isNaN(Number(props.menuPaddingLeft))) {
            menuStyle["paddingLeft"] = props.menuPaddingLeft;
        }
        if (!isNaN(Number(props.menuPaddingBottom))) {
            menuStyle["paddingBottom"] = props.menuPaddingBottom;
        }
        if (!isNaN(Number(props.menuPaddingRight))) {
            menuStyle["paddingRight"] = props.menuPaddingRight;
        }

        // FIXME: If a menu uses getDefaultProps it clobbers the onFinished
        // property set here so you can't close the menu from a button click!
        const menu = (

                    { chevron }
                    <Element {...props} onFinished={closeMenu} onResize={windowResize} />

        );

        ReactDOM.render(menu, this.getOrCreateContainer());

        return {close: closeMenu};
    },
};
<|endoftext|>


<commit_before>// Auto-expand accordian if url contains a valid hash
(function(doc){
    "use strict";

    var DOMReady = function(a){
        var b = doc, c = 'addEventListener';
        b[c] ? b[c]('DOMContentLoaded', a): window.attachEvent('onload', a);
    };

    DOMReady(function() {
        var section, hash = window.location.hash;
        if(hash){
            section = doc.querySelector(hash);
            section.parentNode.querySelector('h3').click();
        }
    });
})(document);
<commit_msg>Use jQuery instead<commit_after>// Auto-expand the accordion and jump to the target if url contains a valid hash
(function(doc){
    "use strict";

    $(function(){
        var $section, hash = window.location.hash;
        if(hash){
            $section = $(hash);
            if($section){
                $(doc).one('hidden.bs.collapse', hash, function(){
                    $section.parent().find('h3').click();
                });
                $('html, body').animate({
                        scrollTop: $(hash).offset().top
                }, 500);
            }
        }
    })
})(document);
<|endoftext|>


<commit_before>/**

 */
(function() {
    function mySetup() {
        for (var config in window.UEDITOR_CONFIG) {
            if (typeof(window.UEDITOR_CONFIG[config]) == 'string')
                window.UEDITOR_CONFIG[config] = window.UEDITOR_CONFIG[config].replace('_test/tools/br/', '');
        }
        var div = document.body.appendChild( document.createElement( 'div' ) );
        $( div ).css( 'width', '500px' ).css( 'height', '500px' ).css( 'border', '1px solid #ccc' );
        var editor = new baidu.editor.Editor({'initialContent':'
欢迎使用ueditor
','autoFloatEnabled':false});

        var ue = new UE.ui.Editor({'UEDITOR_HOME_URL':'../../../','autoFloatEnabled':true});

//        setTimeout(function(){
            editor.render(div);
            setTimeout(function(){
                var range = new baidu.editor.dom.Range( editor.document );
                te.dom.push( div );
                te.obj.push( editor );
                te.obj.push(range);
                te.obj.push( ue );
            },50);
//        },20);
        stop();
        document.getElementsByClassName = function(eleClassName) {
            var getEleClass = [];//定义一个数组
            var myclass = new RegExp("\\b" + eleClassName + "\\b");//创建一个正则表达式对像
            var elem = this.getElementsByTagName("*");//获取文档里所有的元素
            for (var h = 0; h < elem.length; h++) {
                var classes = elem[h].className;//获取class对像
                if (myclass.test(classes)) getEleClass.push(elem[h]);//正则比较，取到想要的CLASS对像
            }
            return getEleClass;//返回数组
        }
    }

    var _d = function() {
        if (te) {
            if (te.dom && te.dom.length) {
                for (var i = 0; i < te.dom.length; i++) {
                    if (te.dom[i] && te.dom[i].parentNode)
                        te.dom[i].parentNode.removeChild(te.dom[i]);
                }

            }
        }
        te.dom = [];
        te.obj = [];
    }
    var s = QUnit.testStart,d = QUnit.testDone;
    QUnit.testStart = function() {
        s.apply( this, arguments );
        mySetup();
    };
    QUnit.testDone = function() {
        _d();
        d.apply(this, arguments);
    }
})();<commit_msg>[case]tools<commit_after>/**

 */
(function() {
    function mySetup() {
        for (var config in window.UEDITOR_CONFIG) {
            if (typeof(window.UEDITOR_CONFIG[config]) == 'string')
                window.UEDITOR_CONFIG[config] = window.UEDITOR_CONFIG[config].replace('_test/tools/br/', '');
        }
        var div = document.body.appendChild( document.createElement( 'div' ) );
        $( div ).css( 'width', '500px' ).css( 'height', '500px' ).css( 'border', '1px solid #ccc' );
        var editor = new baidu.editor.Editor({'initialContent':'
欢迎使用ueditor
','autoFloatEnabled':false});
        var ue = new UE.ui.Editor({'UEDITOR_HOME_URL':'../../../','autoFloatEnabled':true});
        editor.render(div);
        editor.ready(function(){
            var range = new baidu.editor.dom.Range( editor.document );
            te.dom.push( div );
            te.obj.push( editor );
            te.obj.push(range);
            te.obj.push( ue );
        });
        stop();
        document.getElementsByClassName = function(eleClassName) {
            var getEleClass = [];//定义一个数组
            var myclass = new RegExp("\\b" + eleClassName + "\\b");//创建一个正则表达式对像
            var elem = this.getElementsByTagName("*");//获取文档里所有的元素
            for (var h = 0; h < elem.length; h++) {
                var classes = elem[h].className;//获取class对像
                if (myclass.test(classes)) getEleClass.push(elem[h]);//正则比较，取到想要的CLASS对像
            }
            return getEleClass;//返回数组
        }
    }

    var _d = function() {
        if (te) {
            if (te.dom && te.dom.length) {
                for (var i = 0; i < te.dom.length; i++) {
                    if (te.dom[i] && te.dom[i].parentNode)
                        te.dom[i].parentNode.removeChild(te.dom[i]);
                }

            }
        }
        te.dom = [];
        te.obj = [];
    }
    var s = QUnit.testStart,d = QUnit.testDone;
    QUnit.testStart = function() {
        s.apply( this, arguments );
        mySetup();
    };
    QUnit.testDone = function() {
        _d();
        d.apply(this, arguments);
    }
})();<|endoftext|>


<commit_before><commit_msg>add 2.5FC as a project<commit_after><|endoftext|>


<commit_before>(function() {
  "use strict";

  //
  //  General
  //

  // Nav

  var navToggle = document.getElementById("navToggle");
  var navClose = document.getElementById("navClose");
  var nav = document.getElementById("nav");

  navToggle.addEventListener("click", function() {
    nav.classList.add("is-open");
  });
  navClose.addEventListener("click", function() {
    nav.classList.remove("is-open");
  });

  //
  //  Contact Page
  //

  // Google Maps

  var googleMaps = document.getElementById("googleMaps");
  var googleMapsIframe = document.getElementById("googleMapsIframe");

  if (googleMaps && googleMapsIframe) {
    googleMaps.addEventListener("click", function() {
      googleMapsIframe.style.pointerEvents = "auto";
    });

    googleMaps.addEventListener("mouseleave", function() {
      googleMapsIframe.style.pointerEvents = "none";
    });
  }

  // Send Contact Form

  var ContactForm = {
    form: null,
    button: null,
    sendForm: function() {
      var contactForm = this.form;
      var fetchURL = this.form.action;
      var fetchParams = {
        method: "POST",
        body: new FormData(contactForm)
      };

      this.sending();

      fetch(fetchURL, fetchParams)
        .then(function(response) {
          return response.json();
        })
        .then(function(json) {
          ContactForm.success(json);
        });
    },
    addEvents: function() {
      if (this.form) {
        this.form.addEventListener(
          "submit",
          function(e) {
            e.preventDefault();
            this.sendForm();
          }.bind(this)
        );
      }
    },
    sending: function() {
      this.button.setAttribute("disabled", true);
    },
    success: function(data) {
      var response = document.createElement("p");
      response.className = "bg-success padding-md marginTop-md color-white";
      response.textContent =
        "Tu consulta ha sido enviada correctamente. Te responderemos a la mayor brevedad posible.";
      this.form.appendChild(response);

      this.button.removeAttribute("disabled");
      this.form.reset();
    },
    error: function(data) {
      var response = document.createElement("p");
      response.className = "bg-danger padding-md marginTop-md color-white";
      response.textContent =
        "Hemos tenido un problema en el envío, por favor envíanos un email o llama al 96 3864100.";
      this.form.appendChild(response);

      this.button.removeAttribute("disabled");
      this.form.reset();
    },
    init: function() {
      this.form = document.getElementById("contactForm");
      this.button = document.getElementById("submitButton");
      this.addEvents();
    }
  };

  ContactForm.init();
})();
<commit_msg>Remove .json()<commit_after>(function() {
  "use strict";

  //
  //  General
  //

  // Nav

  var navToggle = document.getElementById("navToggle");
  var navClose = document.getElementById("navClose");
  var nav = document.getElementById("nav");

  navToggle.addEventListener("click", function() {
    nav.classList.add("is-open");
  });
  navClose.addEventListener("click", function() {
    nav.classList.remove("is-open");
  });

  //
  //  Contact Page
  //

  // Google Maps

  var googleMaps = document.getElementById("googleMaps");
  var googleMapsIframe = document.getElementById("googleMapsIframe");

  if (googleMaps && googleMapsIframe) {
    googleMaps.addEventListener("click", function() {
      googleMapsIframe.style.pointerEvents = "auto";
    });

    googleMaps.addEventListener("mouseleave", function() {
      googleMapsIframe.style.pointerEvents = "none";
    });
  }

  // Send Contact Form

  var ContactForm = {
    form: null,
    button: null,
    sendForm: function() {
      var contactForm = this.form;
      var fetchURL = this.form.action;
      var fetchParams = {
        method: "POST",
        body: new FormData(contactForm)
      };

      this.sending();

      fetch(fetchURL, fetchParams)
        .then(function() {
          ContactForm.success();
        })
        .catch(function() {
          ContactForm.error();
        });
    },
    addEvents: function() {
      if (this.form) {
        this.form.addEventListener(
          "submit",
          function(e) {
            e.preventDefault();
            this.sendForm();
          }.bind(this)
        );
      }
    },
    sending: function() {
      this.button.setAttribute("disabled", true);
    },
    success: function() {
      var response = document.createElement("p");
      response.className = "bg-success padding-md marginTop-md color-white";
      response.textContent =
        "Tu consulta ha sido enviada correctamente. Te responderemos a la mayor brevedad posible.";
      this.form.appendChild(response);

      this.button.removeAttribute("disabled");
      this.form.reset();
    },
    error: function() {
      var response = document.createElement("p");
      response.className = "bg-danger padding-md marginTop-md color-white";
      response.textContent =
        "Hemos tenido un problema en el envío, por favor envíanos un email o llama al 96 3864100.";
      this.form.appendChild(response);

      this.button.removeAttribute("disabled");
      this.form.reset();
    },
    init: function() {
      this.form = document.getElementById("contactForm");
      this.button = document.getElementById("submitButton");
      this.addEvents();
    }
  };

  ContactForm.init();
})();
<|endoftext|>