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

import './minimap.css';

class Minimap extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mousedown: false
        };
    }
    componentWillMount() {
        let minimapElement = document.getElementsByClassName('minimap-treeview')[0];
        let hasRegionBody = document.getElementsByClassName('miniregion')[0];
        if (typeof (minimapElement) !== 'undefined' && minimapElement !== null) {
            minimapElement.parentNode.removeChild(minimapElement);
        }
        if (typeof (hasRegionBody) !== 'undefined' && hasRegionBody !== null) {
            hasRegionBody.parentNode.removeChild(hasRegionBody);
        }
    }
    componentDidMount() {
        const miniElement = document.getElementById(this.props.rootId);
        if (typeof (miniElement) === 'undefined' && miniElement === null)
            return false;
        let minimapElement = document.createElement('div');
        minimapElement.className = 'minimap-treeview';
        document.body.appendChild(minimapElement);
        let cloneDiv = miniElement.cloneNode(true);
        cloneDiv.className = "minimap noselect " + this.props.rootId;
        let miniregion = document.createElement('div');
        miniregion.className = 'miniregion';
        document.body.appendChild(miniregion);
        minimapElement.appendChild(cloneDiv);
        let mElement = document.getElementsByClassName('minimap')[0];
        let minimapOveraly = document.createElement('div');
        minimapOveraly.className = 'minimap-overlay';
        mElement.appendChild(minimapOveraly);
        this.onResizeHandler(mElement);
        window.addEventListener('resize', this.onResizeHandler.bind(this, miniElement), false);
        window.addEventListener('scroll', this.onScrollHandler.bind(this), false);
        miniregion.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        miniregion.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        miniregion.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        miniregion.addEventListener('click', this.onClickHandler.bind(this), false);
        mElement.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        mElement.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        mElement.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        mElement.addEventListener('click', this.onClickHandler.bind(this), false);
        minimapElement.addEventListener('click', this.onClickHandler.bind(this), false);
    }
    componentDidUpdate() {
        let hasClassBody = document.getElementsByClassName('minimap')[0];
        let hasRegionBody = document.getElementsByClassName('miniregion')[0];
        hasRegionBody.parentNode.removeChild(hasRegionBody);
        hasClassBody.parentNode.removeChild(hasClassBody);
        const miniElement = document.getElementById(this.props.rootId);
        let minimapElement = document.getElementsByClassName('minimap-treeview')[0];
        let cloneDiv = miniElement.cloneNode(miniElement);
        cloneDiv.className = 'minimap noselect ' + this.props.rootId;
        let miniregion = document.createElement('div');
        miniregion.className = 'miniregion';
        document.body.appendChild(miniregion);
        minimapElement.appendChild(cloneDiv);
        let mElement = document.getElementsByClassName('minimap')[0];
        let minimapOveraly = document.createElement('div');
        minimapOveraly.className = 'minimap-overlay';
        mElement.appendChild(minimapOveraly);
        this.onResizeHandler(this);
        mElement.addEventListener('mousemove', this.onMouseMoveHandler.bind(this));
        mElement.addEventListener('click', this.onClickHandler.bind(this), false);
        miniregion.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        miniregion.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        miniregion.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        miniregion.addEventListener('click', this.onClickHandler.bind(this), false);
    }
    onResizeHandler(e) {
        const {offsetHeightRatio, offsetWidthRatio, widthRatio, heightRatio, style, position, toggle} = this.props;
        if (!toggle)
            return false;
        const minimap = document.getElementsByClassName('minimap')[0];
        const minimapOverlay = document.getElementsByClassName('minimap-overlay')[0];
        const minimapTree = document.getElementsByClassName('minimap-treeview')[0];
        let scale = this.scale(e);
        let animation, width, height, top, leftRight, styles,
                offsetHeight = document.documentElement.clientHeight * offsetHeightRatio,
                offsetWidth = document.documentElement.clientWidth * offsetWidthRatio;
        width = document.getElementById(this.props.rootId).clientWidth * (1 / scale.x) * widthRatio;
        height = document.getElementById(this.props.rootId).clientHeight * (1 / scale.y) * heightRatio;
        top = minimap.clientHeight * (scale.y - 1) / 2 + offsetHeight;
        leftRight = minimap.clientWidth * (scale.x - 1) / 2 + offsetWidth;
        let Right = document.documentElement.clientWidth * offsetWidthRatio + 2;
//        if (document.body.scrollHeight > document.getElementById(this.props.rootId).clientHeight) {
//            animation = 'scale3d(0.12,0.12,1)';
//            styles = {
//                '-webkit-transform': animation,
//                '-moz-transform': animation,
//                '-ms-transform': animation,
//                '-o-transform': animation,
//                transforn: animation,
//                width: width + 'px',
//                height: height + 'px',
//                margin: '0px',
//                padding: '0px',
//                'transform-origin': '-35px 0%'
//            };
//        } else {
            animation = 'scale3d(' + scale.x + ',' + scale.y + ',1)';
            styles = {
                '-webkit-transform': animation,
                '-moz-transform': animation,
                '-ms-transform': animation,
                '-o-transform': animation,
                transforn: animation,
                width: width + 'px',
                height: height + 'px',
                margin: '0px',
                padding: '0px',
                top: top + 'px'
            };
            //styles[position] = leftRight + 'px';
       // }

        Object.assign(minimap.style, this._mergeStyles(styles, style));
        Object.assign(minimapOverlay.style, {height: height + 'px', width: '475px'});
        let treeStyle = {height: document.documentElement.clientHeight+ 'px', width:(width/(2/scale.x) * 5 / 2) + 'px'};
        treeStyle[position] = '5px';
        Object.assign(minimapTree.style, this._mergeStyles(treeStyle, style));
        this._miniRegion(e);
    }
    onScrollHandler() {
        let minimap = document.getElementsByClassName('minimap')[0];
        let minregion = document.getElementsByClassName('miniregion')[0];
        const {offsetHeightRatio} = this.props;
        minimap = minimap.getBoundingClientRect();
        let scale = this.scale();
        let offsetHeight = window.innerHeight * offsetHeightRatio;
        let offsetTop = minimap.top * scale.y;
        let offsetPosition = (window.pageYOffset) * scale.y;
        let regionHeight = minregion.offsetHeight;
        let regionBottom = minimap.offsetHeight * scale.y + offsetTop;

        if (offsetPosition + regionHeight + offsetHeight < offsetTop || offsetPosition > regionBottom) {
            Object.assign(minregion.style, {display: 'none'});
        } else {
            Object.assign(minregion.style, {top: offsetPosition + offsetHeight - offsetTop + 'px', display: 'block'});
        }

    }
    onClickHandler(e) {
        this._scrollTop(e);
        this.setState({mousedown: false});
    }
    onMouseMoveHandler(e) {
        const mouseDown = this.state.mousedown;
        if (!mouseDown) {
            this.setState({mousedown: false});
            return false;
        }
        this._scrollTop(e);
    }
    onMouseDownHandler(e) {
        this.setState({mousedown: true});
        let region = document.getElementsByClassName('miniregion')[0];
        let minimap = document.getElementsByClassName('minimap')[0];
        this.addClass(region, 'dragging');
        this.addClass(minimap, 'noselect');
    }
    onMouseUpHandler(e) {
        let region = document.getElementsByClassName('miniregion')[0];
        let minimap = document.getElementsByClassName('minimap')[0];
        this.removeClass(region, 'dragging');
        this.removeClass(minimap, 'noselect');
        this.setState({mousedown: false});
    }
    scale() {
        const {widthRatio, heightRatio} = this.props;
        let documentElement = document.getElementsByClassName(this.props.rootId)[0], minimap = document.getElementsByClassName('minimap')[0];

        return {
            x: (documentElement.clientWidth / minimap.clientWidth) * widthRatio,
            y: (documentElement.clientHeight / minimap.clientHeight) * heightRatio
        };
    }
    onScrollChecker(element) {
        return document.getElementById(element).scrollHeight > document.getElementById(element).clientHeight;
    }
    _mergeStyles(...args) {
        return Object.assign({}, ...args);
    }
    _miniRegion() {
        const {offsetHeightRatio, offsetWidthRatio, position, style} = this.props;
        const minimap = document.getElementsByClassName('minimap')[0].getBoundingClientRect();
        let scale = this.scale();
        let regionTop = minimap.top * scale.y;
        let offsetWidth = document.documentElement.clientWidth * offsetWidthRatio + 4;
        let width = document.getElementsByClassName('minimap-treeview')[0].clientWidth + 4;
//        if (document.body.scrollHeight > document.getElementById(this.props.rootId).clientHeight) {
//            Object.assign(document.getElementsByClassName('miniregion')[0].style, {width: '138px', height: '125px', top: '15px', right: offsetWidth + 'px'});
//            return false;
//        }
        let styles = {
            width:  width  + 'px',
            height: window.innerHeight * scale.y + 'px',
            margin: '0px',
            top: window.pageYOffset * scale.y + document.documentElement.clientHeight * offsetHeightRatio - regionTop + 'px',
            display: 'block'
        };
        styles[position] = offsetWidth + 'px';
        Object.assign(document.getElementsByClassName('miniregion')[0].style, this._mergeStyles(styles, style));
    }
    _scrollTop(e) {
        const {offsetHeightRatio, toggle, smoothScrollDelay, smoothScroll} = this.props;
        if (!toggle)
            return false;
        const minimap = document.getElementsByClassName('minimap')[0].getBoundingClientRect();
        let scale = this.scale();
        let offsetHeight = document.documentElement.clientHeight * offsetHeightRatio;
        let offsetTop = minimap.top * scale.y;
        let regionHeight = document.getElementsByClassName('miniregion')[0].offsetHeight;
        let target = (e.clientY - regionHeight / 2 - offsetHeight + offsetTop) / scale.y;
        if (e.type === 'click' && smoothScroll) {
            let current = window.pageYOffset;
            let maxTarget = document.getElementsByClassName('minimap')[0].offsetHeight;
            target = Math.max(target, Math.min(target, maxTarget));
            let direction = target > current;
            let delay = smoothScrollDelay;
            let distance = Math.abs(current - target);
            let r = delay / distance;
            let unitScroll = 1;
            let unitDelay = 4;

            if (r >= 4) {
                unitDelay = parseInt(unitScroll, 15);
            } else if (r >= 1) {
                unitScroll = parseInt(r, 15) * 4;
            } else {
                unitScroll = parseInt((4 / r),15);
            }
            let next = current;
            let count = parseInt(distance / unitScroll,8);
            var smoothScrolling = () => {
                next = next + (direction ? unitScroll : -unitScroll);
                if (--count <= 0) {
                    clearInterval(timer);
                    next = target;
                }
                console.log(next);
                window.scrollTo(0, next);
            };
            let timer = window.setInterval(smoothScrolling, unitDelay);
        } else {
            window.scrollTo(0, target);
        }
        e.stopPropagation();
    }

    addClass(el, className) {
        if (el.className)
            el.classList.add(className);
        else if (!this.hasClass(el, className))
            el.className += " " + className;

    }
    hasClass(el, className) {
        if (el.classList)
            return el.classList.contains(className);
        else
            return !!el.className.match(new RegExp('(\\b' + className + '(\\b)'));
    }
    removeClass(el, className) {
        if (el.classList)
            el.classList.remove(className);
        else if (this.hasClass(el, className)) {
            let reg = new RegExp('(\\b' + className + '(\\b)');
            el.className = el.className.replace(reg, ' ');
        }
    }
    render() {
        return

;
    }
}
Minimap.defaultProps = {
    heightRatio: 0.1,
    widthRatio: 0.07,
    offsetHeightRatio: 0.010,
    offsetWidthRatio: 0.0,
    position: 'right',
    smoothScroll: true,
    smoothScrollDelay: 200,
    toggle: true
};
Minimap.propTypes = {
    heightRatio: PropTypes.number,
    widthRatio: PropTypes.number,
    offsetHeightRatio: PropTypes.number,
    offsetWidthRatio: PropTypes.number,
    position: PropTypes.string,
    smoothScroll: PropTypes.bool,
    smoothScrollDelay: PropTypes.number,
    toggle: PropTypes.bool,
    rootId: PropTypes.string.isRequired
};
export default Minimap;<commit_msg>code updated<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import './minimap.css';

class Minimap extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mousedown: false
        };
    }
    componentWillMount() {
        let minimapElement = document.getElementsByClassName('minimap-treeview')[0];
        let hasRegionBody = document.getElementsByClassName('miniregion')[0];
        if (typeof (minimapElement) !== 'undefined' && minimapElement !== null) {
            minimapElement.parentNode.removeChild(minimapElement);
        }
        if (typeof (hasRegionBody) !== 'undefined' && hasRegionBody !== null) {
            hasRegionBody.parentNode.removeChild(hasRegionBody);
        }
    }
    componentDidMount() {
        const miniElement = document.getElementById(this.props.rootId);
        if (typeof (miniElement) === 'undefined' && miniElement === null)
            return false;
        let minimapElement = document.createElement('div');
        minimapElement.className = 'minimap-treeview';
        document.body.appendChild(minimapElement);
        let cloneDiv = miniElement.cloneNode(true);
        cloneDiv.className = "minimap noselect " + this.props.rootId;
        let miniregion = document.createElement('div');
        miniregion.className = 'miniregion';
        document.body.appendChild(miniregion);
        minimapElement.appendChild(cloneDiv);
        let mElement = document.getElementsByClassName('minimap')[0];
        let minimapOveraly = document.createElement('div');
        minimapOveraly.className = 'minimap-overlay';
        mElement.appendChild(minimapOveraly);
        this.onResizeHandler(mElement);
        window.addEventListener('resize', this.onResizeHandler.bind(this, miniElement), false);
        window.addEventListener('scroll', this.onScrollHandler.bind(this), false);
        miniregion.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        miniregion.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        miniregion.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        miniregion.addEventListener('click', this.onClickHandler.bind(this), false);
        mElement.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        mElement.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        mElement.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        mElement.addEventListener('click', this.onClickHandler.bind(this), false);
        minimapElement.addEventListener('click', this.onClickHandler.bind(this), false);
    }
    componentDidUpdate() {
        let hasClassBody = document.getElementsByClassName('minimap')[0];
        let hasRegionBody = document.getElementsByClassName('miniregion')[0];
        hasRegionBody.parentNode.removeChild(hasRegionBody);
        hasClassBody.parentNode.removeChild(hasClassBody);
        const miniElement = document.getElementById(this.props.rootId);
        let minimapElement = document.getElementsByClassName('minimap-treeview')[0];
        let cloneDiv = miniElement.cloneNode(miniElement);
        cloneDiv.className = 'minimap noselect ' + this.props.rootId;
        let miniregion = document.createElement('div');
        miniregion.className = 'miniregion';
        document.body.appendChild(miniregion);
        minimapElement.appendChild(cloneDiv);
        let mElement = document.getElementsByClassName('minimap')[0];
        let minimapOveraly = document.createElement('div');
        minimapOveraly.className = 'minimap-overlay';
        mElement.appendChild(minimapOveraly);
        this.onResizeHandler(this);
        mElement.addEventListener('mousemove', this.onMouseMoveHandler.bind(this));
        mElement.addEventListener('click', this.onClickHandler.bind(this), false);
        miniregion.addEventListener('mousedown', this.onMouseDownHandler.bind(this), false);
        miniregion.addEventListener('mouseup', this.onMouseUpHandler.bind(this), false);
        miniregion.addEventListener('mousemove', this.onMouseMoveHandler.bind(this), false);
        miniregion.addEventListener('click', this.onClickHandler.bind(this), false);
    }
    onResizeHandler(e) {
        const {offsetHeightRatio, offsetWidthRatio, widthRatio, heightRatio, style, position, toggle} = this.props;
        if (!toggle)
            return false;
        const minimap = document.getElementsByClassName('minimap')[0];
        const minimapOverlay = document.getElementsByClassName('minimap-overlay')[0];
        const minimapTree = document.getElementsByClassName('minimap-treeview')[0];
        let scale = this.scale(e);
        let animation, width, height, top, leftRight, styles,
                offsetHeight = document.documentElement.clientHeight * offsetHeightRatio,
                offsetWidth = document.documentElement.clientWidth * offsetWidthRatio;
        width = document.getElementById(this.props.rootId).clientWidth * (1 / scale.x) * widthRatio;
        height = document.getElementById(this.props.rootId).clientHeight * (1 / scale.y) * heightRatio;
        top = minimap.clientHeight * (scale.y - 1) / 2 + offsetHeight;
        leftRight = minimap.clientWidth * (scale.x - 1) / 2 + offsetWidth;
        let Right = document.documentElement.clientWidth * offsetWidthRatio + 2;
//        if (document.body.scrollHeight > document.getElementById(this.props.rootId).clientHeight) {
//            animation = 'scale3d(0.12,0.12,1)';
//            styles = {
//                '-webkit-transform': animation,
//                '-moz-transform': animation,
//                '-ms-transform': animation,
//                '-o-transform': animation,
//                transforn: animation,
//                width: width + 'px',
//                height: height + 'px',
//                margin: '0px',
//                padding: '0px',
//                'transform-origin': '-35px 0%'
//            };
//        } else {
            animation = 'scale3d(' + scale.x + ',' + scale.y + ',1)';
            styles = {
                '-webkit-transform': animation,
                '-moz-transform': animation,
                '-ms-transform': animation,
                '-o-transform': animation,
                transforn: animation,
                width: width + 'px',
                height: height + 'px',
                margin: '0px',
                padding: '0px',
                top: top + 'px'
            };
            //styles[position] = leftRight + 'px';
       // }

        Object.assign(minimap.style, this._mergeStyles(styles, style));
        Object.assign(minimapOverlay.style, {height: height + 'px', width: '475px'});
        let treeStyle = {height: document.documentElement.clientHeight+ 'px', width:(width/(2/scale.x) * 5 / 2) + 'px'};
        treeStyle[position] = '5px';
        Object.assign(minimapTree.style, this._mergeStyles(treeStyle, style));
        this._miniRegion(e);
    }
    onScrollHandler() {
        let minimap = document.getElementsByClassName('minimap')[0];
        let minregion = document.getElementsByClassName('miniregion')[0];
        const {offsetHeightRatio} = this.props;
        minimap = minimap.getBoundingClientRect();
        let scale = this.scale();
        let offsetHeight = window.innerHeight * offsetHeightRatio;
        let offsetTop = minimap.top * scale.y;
        let offsetPosition = (window.pageYOffset) * scale.y;
        let regionHeight = minregion.offsetHeight;
        let regionBottom = minimap.offsetHeight * scale.y + offsetTop;

        if (offsetPosition + regionHeight + offsetHeight < offsetTop || offsetPosition > regionBottom) {
            Object.assign(minregion.style, {display: 'none'});
        } else {
            Object.assign(minregion.style, {top: offsetPosition + offsetHeight - offsetTop + 'px', display: 'block'});
        }

    }
    onClickHandler(e) {
        this._scrollTop(e);
        this.setState({mousedown: false});
    }
    onMouseMoveHandler(e) {
        const mouseDown = this.state.mousedown;
        if (!mouseDown) {
            this.setState({mousedown: false});
            return false;
        }
        this._scrollTop(e);
    }
    onMouseDownHandler(e) {
        this.setState({mousedown: true});
        let region = document.getElementsByClassName('miniregion')[0];
        let minimap = document.getElementsByClassName('minimap')[0];
        this.addClass(region, 'dragging');
        this.addClass(minimap, 'noselect');
    }
    onMouseUpHandler(e) {
        let region = document.getElementsByClassName('miniregion')[0];
        let minimap = document.getElementsByClassName('minimap')[0];
        this.removeClass(region, 'dragging');
        this.removeClass(minimap, 'noselect');
        this.setState({mousedown: false});
    }
    scale() {
        const {widthRatio, heightRatio} = this.props;
        let documentElement = document.getElementsByClassName(this.props.rootId)[0], minimap = document.getElementsByClassName('minimap')[0];

        return {
            x: (documentElement.clientWidth / minimap.clientWidth) * widthRatio,
            y: (documentElement.clientHeight / minimap.clientHeight) * heightRatio
        };
    }
    onScrollChecker(element) {
        return document.getElementById(element).scrollHeight > document.getElementById(element).clientHeight;
    }
    _mergeStyles(...args) {
        return Object.assign({}, ...args);
    }
    _miniRegion() {
        const {offsetHeightRatio, offsetWidthRatio, position, style} = this.props;
        const minimap = document.getElementsByClassName('minimap')[0].getBoundingClientRect();
        let scale = this.scale();
        let regionTop = minimap.top * scale.y;
        let offsetWidth = document.documentElement.clientWidth * offsetWidthRatio + 4;
        let width = document.getElementsByClassName('minimap-treeview')[0].clientWidth + 4;
//        if (document.body.scrollHeight > document.getElementById(this.props.rootId).clientHeight) {
//            Object.assign(document.getElementsByClassName('miniregion')[0].style, {width: '138px', height: '125px', top: '15px', right: offsetWidth + 'px'});
//            return false;
//        }
        let styles = {
            width:  width  + 'px',
            height: window.innerHeight * scale.y + 'px',
            margin: '0px',
            top: window.pageYOffset * scale.y + document.documentElement.clientHeight * offsetHeightRatio - regionTop + 'px',
            display: 'block'
        };
        styles[position] = offsetWidth + 'px';
        Object.assign(document.getElementsByClassName('miniregion')[0].style, this._mergeStyles(styles, style));
    }
    _scrollTop(e) {
        const {offsetHeightRatio, toggle, smoothScrollDelay, smoothScroll} = this.props;
        if (!toggle)
            return false;
        const minimap = document.getElementsByClassName('minimap')[0].getBoundingClientRect();
        let scale = this.scale();
        let offsetHeight = document.documentElement.clientHeight * offsetHeightRatio;
        let offsetTop = minimap.top * scale.y;
        let regionHeight = document.getElementsByClassName('miniregion')[0].offsetHeight;
        let target = (e.clientY - regionHeight / 2 - offsetHeight + offsetTop) / scale.y;
        if (e.type === 'click' && smoothScroll) {
            let current = window.pageYOffset;
            let maxTarget = document.getElementsByClassName('minimap')[0].offsetHeight;
            target = Math.max(target, Math.min(target, maxTarget));
            let direction = target > current;
            let delay = smoothScrollDelay;
            let distance = Math.abs(current - target);
            let r = delay / distance;
            let unitScroll = 1;
            let unitDelay = 4;

            if (r >= 4) {
                unitDelay = parseInt(unitScroll, 15);
            } else if (r >= 1) {
                unitScroll = parseInt(r, 15) * 4;
            } else {
                unitScroll = parseInt((4 / r),15);
            }
            let next = current;
            let count = parseInt(distance / unitScroll,8);
            var smoothScrolling = () => {
                next = next + (direction ? unitScroll : -unitScroll);
                if (--count <= 0) {
                    clearInterval(timer);
                    next = target;
                }
                console.log(next);
                window.scrollTo(0, next);
            };
            let timer = window.setInterval(smoothScrolling, unitDelay);
        } else {
            window.scrollTo(0, target);
        }
        e.stopPropagation();
    }

    addClass(el, className) {
        if (el.className)
            el.classList.add(className);
        else if (!this.hasClass(el, className))
            el.className += " " + className;

    }
    hasClass(el, className) {
        if (el.classList)
            return el.classList.contains(className);
        else
            return !!el.className.match(new RegExp('(\\b' + className + '(\\b)'));
    }
    removeClass(el, className) {
        if (el.classList)
            el.classList.remove(className);
        else if (this.hasClass(el, className)) {
            let reg = new RegExp('(\\b' + className + '(\\b)');
            el.className = el.className.replace(reg, ' ');
        }
    }
    render() {
        return

;
    }
}
Minimap.defaultProps = {
    heightRatio: 0.1,
    widthRatio: 0.07,
    offsetHeightRatio: 0.010,
    offsetWidthRatio: 0.0,
    position: 'right',
    smoothScroll: true,
    smoothScrollDelay: 200,
    toggle: true
};
Minimap.propTypes = {
    heightRatio: PropTypes.number,
    widthRatio: PropTypes.number,
    offsetHeightRatio: PropTypes.number,
    offsetWidthRatio: PropTypes.number,
    position: PropTypes.string,
    smoothScroll: PropTypes.bool,
    smoothScrollDelay: PropTypes.number,
    toggle: PropTypes.bool,
    rootId: PropTypes.string.isRequired
};
export default Minimap;<|endoftext|>


<commit_before>'use strict';

function World(numCreatures, canvas, synaptic) {
	var that = this;

	this.width = canvas.width;
	this.height = canvas.height;
	this.ctx = canvas.getContext('2d');
	this.maxCreatures = numCreatures;

	// populate
	this.creatures = function (numCreatures, synaptic) {

		var squashingFunctions = [
			synaptic.Neuron.squash.LOGISTIC,
			synaptic.Neuron.squash.TANH,
			synaptic.Neuron.squash.HLIM,
			synaptic.Neuron.squash.IDENTITY
		];

		var creatures = [];

		for (var i = 0; i < numCreatures; i++) {

			var x = Math.random() * (that.width - 100) + 50;
			var y = Math.random() * (that.height - 100) + 50;

			var network = new synaptic.Architect.Perceptron(5, 4, 3, 2);

			// randomize the activation functions
			network.neurons().forEach(function (neuron) {
				if (neuron.layer === 'output') {
					neuron.neuron.squash = synaptic.Neuron.squash.TANH;
				}
				else {
					neuron.neuron.squash = squashingFunctions[Math.floor(Math.random() * squashingFunctions.length)];
				}
			});

			creatures[i] = new Creature(network, that, x, y);
			creatures[i].velocity.random();
		}

		return creatures;

	}(numCreatures, synaptic);

	this.foodDensity = .001;
	this.food = [];
	for (var i = 0; i < (this.width * this.height * this.foodDensity); i++) {
		this.food.push(null);
	}

	this.generation = 1;
	this.era = 1;
	that.mostFit = 0;
	that.averageFoodCollected = 0;
	this.numSpecies = numCreatures;
	this.ticks = 1;

	this.overlay = new Overlay(this);

	var loop = function () {

		drawBackground();

		// add food
		for (var i = 0; i < that.food.length; i++) {
			if (that.food[i] === null) {
				that.food[i] = new Vector(null, null);
			}

			if (that.food[i].x === null || that.food[i].y === null) {
				that.food[i].x = Math.random() * (that.width - 100) + 50;
				that.food[i].y = Math.random() * (that.height - 100) + 50;
			}

			that.ctx.beginPath();
			that.ctx.fillStyle = '#DDDDDD';
			that.ctx.arc(that.food[i].x, that.food[i].y, 3, 0, 2 * Math.PI);
			that.ctx.fill();
		}

		// update each creature
		that.creatures.forEach(function (creature) {
			creature.tick();
			creature.draw();
		});

		that.ticks++;

		if (that.ticks % 300 == 0) {

			console.log("new generation");
			that.generation++;

			// remove dead creatures and sort remaining by fitness
			var newCreatures = that.creatures.filter(function (creature) {
				return creature.isAlive();
			}).sort(function (a, b) {
				return b.fitness() - a.fitness();
			});
			console.log("Removed " + (that.creatures.length - newCreatures.length) + " dead creatures");

			// remove unfit
			var numUnfitRemoved = 0;
			while (newCreatures.length > that.maxCreatures / 2) {
				newCreatures.pop();
				++numUnfitRemoved;
			}
			console.log("Removed " + numUnfitRemoved + " unfit creatures");

			// reproduce
			var numToClone = newCreatures.length;
			for (var i = 0; i < numToClone; i++) {
				newCreatures.push(newCreatures[i].clone());
			}
			console.log(numToClone + " creatures reproduced");

			// collect stats
			var colors = {};
			for (var i = 0; i < newCreatures.length; i++) {
				colors[newCreatures[i].color] = colors[newCreatures[i].color] ? colors[newCreatures[i].color]++ : 1;
			}

			that.numSpecies = Object.keys(colors).length;
			if (that.numSpecies == 1) {
				that.era++;

				// randomize colors
				for (var i = 0; i < newCreatures.length; i++) {
					newCreatures[i].color = 'rgb(' +
						Math.floor(Math.random() * 255) + ',' +
						Math.floor(Math.random() * 255) + ',' +
						Math.floor(Math.random() * 255) + ')';
				}

				that.numSpecies = that.numCreatures;
			}

			// reset
			newCreatures.forEach(function (creature) {
				creature.reset();
			});

			that.creatures = newCreatures;
			that.ticks = 1;
		}

		that.overlay.draw();

		setTimeout(loop, 10);
	}

	var drawBackground = function () {
		//that.ctx.globalAlpha = 0.2;
		that.ctx.fillStyle = '#f4f4f4';
		that.ctx.fillRect(0, 0, that.width, that.height);
		//that.ctx.globalAlpha = 1;
	}

	this.start = function() {

		return loop();
	}
}
<commit_msg>Perf improvements?<commit_after>'use strict';

function World(numCreatures, canvas, synaptic) {
	var that = this;

	this.width = canvas.width;
	this.height = canvas.height;
	this.ctx = canvas.getContext('2d');
	this.maxCreatures = numCreatures;

	// populate
	this.creatures = function (numCreatures, synaptic) {

		var squashingFunctions = [
			synaptic.Neuron.squash.LOGISTIC,
			synaptic.Neuron.squash.TANH,
			synaptic.Neuron.squash.HLIM,
			synaptic.Neuron.squash.IDENTITY
		];

		var creatures = [];

		for (var i = 0; i < numCreatures; i++) {

			var x = Math.random() * (that.width - 100) + 50;
			var y = Math.random() * (that.height - 100) + 50;

			var network = new synaptic.Architect.Perceptron(5, 10, 2);

			// randomize the activation functions
			network.neurons().forEach(function (neuron) {
				if (neuron.layer === 'output') {
					neuron.neuron.squash = synaptic.Neuron.squash.TANH;
				}
				else {
					neuron.neuron.squash = squashingFunctions[Math.floor(Math.random() * squashingFunctions.length)];
				}
			});

			creatures[i] = new Creature(network, that, x, y);
			creatures[i].velocity.random();
		}

		return creatures;

	}(numCreatures, synaptic);

	this.foodDensity = .001;
	this.food = [];
	for (var i = 0; i < (this.width * this.height * this.foodDensity); i++) {
		this.food.push(null);
	}

	this.generation = 1;
	this.era = 1;
	that.mostFit = 0;
	that.averageFoodCollected = 0;
	this.numSpecies = numCreatures;
	this.ticks = 1;

	this.overlay = new Overlay(this);

	var loop = function () {

		drawBackground();

		// add food
		for (var i = 0; i < that.food.length; i++) {
			if (that.food[i] === null) {
				that.food[i] = new Vector(null, null);
			}

			if (that.food[i].x === null || that.food[i].y === null) {
				that.food[i].x = Math.random() * (that.width - 100) + 50;
				that.food[i].y = Math.random() * (that.height - 100) + 50;
			}

			that.ctx.beginPath();
			that.ctx.fillStyle = '#DDDDDD';
			that.ctx.arc(that.food[i].x, that.food[i].y, 3, 0, 2 * Math.PI);
			that.ctx.fill();
		}

		// update each creature
		that.creatures.forEach(function (creature) {
			creature.tick();
			creature.draw();
		});

		that.ticks++;

		if (that.ticks % 300 === 0) {
			newGeneration();
		}

		that.overlay.draw();

		setTimeout(loop, 10);
	}

	var newGeneration = function () {
		console.log("new generation");
		that.generation++;

		// remove dead creatures and sort remaining by fitness
		var newCreatures = that.creatures.filter(function (creature) {
			return creature.isAlive();
		}).sort(function (a, b) {
			return b.fitness() - a.fitness();
		});
		console.log("Removed " + (that.creatures.length - newCreatures.length) + " dead creatures");

		// remove unfit
		var numUnfitRemoved = 0;
		while (newCreatures.length > that.maxCreatures / 2) {
			newCreatures.pop();
			++numUnfitRemoved;
		}
		console.log("Removed " + numUnfitRemoved + " unfit creatures");

		// reproduce
		var numToClone = newCreatures.length;
		for (var i = 0; i < numToClone; i++) {
			newCreatures.push(newCreatures[i].clone());
		}
		console.log(numToClone + " creatures reproduced");

		// collect stats
		var colors = {};
		for (var i = 0; i < newCreatures.length; i++) {
			colors[newCreatures[i].color] = colors[newCreatures[i].color] ? colors[newCreatures[i].color]++ : 1;
		}

		that.numSpecies = Object.keys(colors).length;
		if (that.numSpecies == 1) {
			that.era++;

			// randomize colors
			for (var i = 0; i < newCreatures.length; i++) {
				newCreatures[i].color = 'rgb(' +
					Math.floor(Math.random() * 255) + ',' +
					Math.floor(Math.random() * 255) + ',' +
					Math.floor(Math.random() * 255) + ')';
			}

			that.numSpecies = that.numCreatures;
		}

		// reset
		newCreatures.forEach(function (creature) {
			creature.reset();
		});

		that.creatures = newCreatures;
		that.ticks = 1;
	}

	var drawBackground = function () {
		//that.ctx.globalAlpha = 0.2;
		that.ctx.fillStyle = '#f4f4f4';
		that.ctx.fillRect(0, 0, that.width, that.height);
		//that.ctx.globalAlpha = 1;
	}

	this.start = function() {

		return loop();
	}
}
<|endoftext|>


<commit_before>//
// Copyright 2012 Shared Learning Collaborative, LLC
//
// 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.
//

//
// Run the indexing script with data in collections to test for errors.
//
// Indexing Gotchas:
// - Long index names
// - Parallel indexes: creating an index key with more than one field that is an array
// - Redundant indexes: {a,b,c} makes {a,b}, {a} redundant
//
// Known problem fields for parallel indexes: (no index key with more
// than one of these)
// These can be found in ComplexTypex.xsd
// xpath=//xs:element[@type="reference"][@maxOccurs="unbounded"]
// - *:metaData.edOrgs
// - *:metaData.teacherContext
// - cohort:body.programId
// - disciplineAction:body.disciplineIncidentId
// - disciplineAction:body.staffId
// - disciplineAction:body.studentId
// - learningObjective:body.learningStandards
// - learningObjective:body.parentLearningObjective
// - reportCard:body.grades
// - reportCard:body.studentCompetencyId
// - section:body.assessmentReferences
// - section:body.programReference
// - session:body.gradingPeriodReference
// - studentAcademicRecord:body.reportCards
//

//app, auth, realm
db["adminDelegation"].ensureIndex({"metaData.tenantId":1,"_id":1});
db["adminDelegation"].ensureIndex({"metaData.tenantId":1,"body.localEdOrgId":1,"body.appApprovalEnabled":1});

db["application"].ensureIndex({"body.admin_visible":1});
db["application"].ensureIndex({"body.allowed_for_all_edorgs":1});
db["application"].ensureIndex({"body.authorized_ed_orgs":1});
db["application"].ensureIndex({"body.authorized_for_all_edorgs":1});
db["application"].ensureIndex({"body.client_secret":1,"body.client_id":1});
db["application"].ensureIndex({"body.name":1});

db["applicationAuthorization"].ensureIndex({"body.appIds":1});
db["applicationAuthorization"].ensureIndex({"body.authId":1});
db["applicationAuthorization"].ensureIndex({"metaData.tenantId":1,"body.authId":1,"body.authType":1});

db["customRole"].ensureIndex({"metaData.tenantId":1,"_id":1});
db["customRole"].ensureIndex({"metaData.tenantId":1,"body.realmId":1});

db["realm"].ensureIndex({"body.idp.id":1});
db["realm"].ensureIndex({"body.uniqueIdentifier":1});
db["realm"].ensureIndex({"metaData.tenantId":1,"body.edOrg":1});

db["securityEvent"].ensureIndex({"metaData.tenantId":1,"body.targetEdOrg":1,"body.roles":1});

db["tenant"].ensureIndex({"body.landingZone.ingestionServer":1,"body.landingZone.preload.status":1});
db["tenant"].ensureIndex({"body.landingZone.path":1});
db["tenant"].ensureIndex({"body.tenantId":1});
db["tenant"].ensureIndex({"type":1});

db["userSession"].ensureIndex({"body.appSession.code.value":1});
db["userSession"].ensureIndex({"body.appSession.samlId":1});
db["userSession"].ensureIndex({"body.appSession.token":1});
db["userSession"].ensureIndex({"body.expiration":1,"body.hardLogout":1,"body.appSession.token":1});
db["userSession"].ensureIndex({"body.principal.externalId":1});
db["userSession"].ensureIndex({"body.principal.realm":1});<commit_msg>Re-commit 0fc492b5cb36bbe3b33d4ac6cc9382fb5a3cf719 since it was reverted accidentally by 73a1837bc873a65ade5e6dcc863e7749c189f77a<commit_after>//
// Copyright 2012 Shared Learning Collaborative, LLC
//
// 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.
//

//
// Run the indexing script with data in collections to test for errors.
//
// Indexing Gotchas:
// - Long index names
// - Parallel indexes: creating an index key with more than one field that is an array
// - Redundant indexes: {a,b,c} makes {a,b}, {a} redundant
//
// Known problem fields for parallel indexes: (no index key with more
// than one of these)
// These can be found in ComplexTypex.xsd
// xpath=//xs:element[@type="reference"][@maxOccurs="unbounded"]
// - *:metaData.edOrgs
// - *:metaData.teacherContext
// - cohort:body.programId
// - disciplineAction:body.disciplineIncidentId
// - disciplineAction:body.staffId
// - disciplineAction:body.studentId
// - learningObjective:body.learningStandards
// - learningObjective:body.parentLearningObjective
// - reportCard:body.grades
// - reportCard:body.studentCompetencyId
// - section:body.assessmentReferences
// - section:body.programReference
// - session:body.gradingPeriodReference
// - studentAcademicRecord:body.reportCards
//

//app, auth, realm
db["adminDelegation"].ensureIndex({"metaData.tenantId":1,"_id":1});
db["adminDelegation"].ensureIndex({"metaData.tenantId":1,"body.localEdOrgId":1,"body.appApprovalEnabled":1});

db["application"].ensureIndex({"body.admin_visible":1});
db["application"].ensureIndex({"body.allowed_for_all_edorgs":1});
db["application"].ensureIndex({"body.authorized_ed_orgs":1});
db["application"].ensureIndex({"body.authorized_for_all_edorgs":1});
db["application"].ensureIndex({"body.client_secret":1,"body.client_id":1});
db["application"].ensureIndex({"body.name":1});

db["applicationAuthorization"].ensureIndex({"body.appIds":1});
db["applicationAuthorization"].ensureIndex({"body.authId":1});
db["applicationAuthorization"].ensureIndex({"metaData.tenantId":1,"body.authId":1,"body.authType":1});

db["customRole"].ensureIndex({"metaData.tenantId":1,"_id":1});
db["customRole"].ensureIndex({"metaData.tenantId":1,"body.realmId":1});

db["realm"].ensureIndex({"body.idp.id":1});
db["realm"].ensureIndex({"body.uniqueIdentifier":1});
db["realm"].ensureIndex({"metaData.tenantId":1,"body.edOrg":1});

db["securityEvent"].ensureIndex({"metaData.tenantId":1,"body.targetEdOrg":1,"body.roles":1});

db["tenant"].ensureIndex({"body.landingZone.ingestionServer":1,"body.landingZone.preload.status":1});
db["tenant"].ensureIndex({"body.landingZone.path":1});
db["tenant"].ensureIndex({"body.tenantId":1},{unique:true});
db["tenant"].ensureIndex({"type":1});

db["userSession"].ensureIndex({"body.appSession.code.value":1});
db["userSession"].ensureIndex({"body.appSession.samlId":1});
db["userSession"].ensureIndex({"body.appSession.token":1});
db["userSession"].ensureIndex({"body.expiration":1,"body.hardLogout":1,"body.appSession.token":1});
db["userSession"].ensureIndex({"body.principal.externalId":1});
db["userSession"].ensureIndex({"body.principal.realm":1});
<|endoftext|>


<commit_before>var React = require('react/addons');
var classNames = require('classnames');
var blacklist = require('blacklist');

var BUTTON_TYPES = [
	'default',
	'default-primary',
	'default-success',
	'default-warning',
	'default-danger',
	'hollow-primary',
	'hollow-success',
	'hollow-warning',
	'hollow-danger',
	'primary',
	'success',
	'warning',
	'danger',
	'link',
	'link-text',
	'link-cancel',
	'link-delete'
];

module.exports = React.createClass({
	displayName: 'ElementalButton',
	propTypes: {
		block: React.PropTypes.bool,
		className: React.PropTypes.string,
		href: React.PropTypes.string,
		onClick: React.PropTypes.func,
		size: React.PropTypes.string,
		submit: React.PropTypes.bool,
		type: React.PropTypes.oneOf(BUTTON_TYPES)
	},
	getDefaultProps() {
		return {
			type: 'default'
		};
	},
	render() {
		// classes
		var componentClass = classNames(
			'Button',
			'Button--' + this.props.type,
			(this.props.size ? 'Button--' + this.props.size : null),
			{
				'Button--block': this.props.block
			},
			this.props.className
		);

		// props
		var props = blacklist(this.props, ['type', 'size', 'className']);
		props.className = componentClass;

		var tag = 'button';
		props.type = this.props.submit ? 'submit' : 'button';

		if (props.href) {
			tag = 'a';
			props.type = null;
		}

		return React.createElement(
			tag,
			props,
			this.props.children
		);
	}
});<commit_msg>update Button displayName<commit_after>var React = require('react/addons');
var classNames = require('classnames');
var blacklist = require('blacklist');

var BUTTON_TYPES = [
	'default',
	'default-primary',
	'default-success',
	'default-warning',
	'default-danger',
	'hollow-primary',
	'hollow-success',
	'hollow-warning',
	'hollow-danger',
	'primary',
	'success',
	'warning',
	'danger',
	'link',
	'link-text',
	'link-cancel',
	'link-delete'
];

module.exports = React.createClass({
	displayName: 'Button',
	propTypes: {
		block: React.PropTypes.bool,
		className: React.PropTypes.string,
		href: React.PropTypes.string,
		onClick: React.PropTypes.func,
		size: React.PropTypes.string,
		submit: React.PropTypes.bool,
		type: React.PropTypes.oneOf(BUTTON_TYPES)
	},
	getDefaultProps() {
		return {
			type: 'default'
		};
	},
	render() {
		// classes
		var componentClass = classNames(
			'Button',
			'Button--' + this.props.type,
			(this.props.size ? 'Button--' + this.props.size : null),
			{
				'Button--block': this.props.block
			},
			this.props.className
		);

		// props
		var props = blacklist(this.props, ['type', 'size', 'className']);
		props.className = componentClass;

		var tag = 'button';
		props.type = this.props.submit ? 'submit' : 'button';

		if (props.href) {
			tag = 'a';
			props.type = null;
		}

		return React.createElement(
			tag,
			props,
			this.props.children
		);
	}
});<|endoftext|>


<commit_before>import Vector from '../vector';
import Label from './label';
import OBB from '../utils/obb';
import Geo from '../geo';

const PLACEMENT = {
    MID_POINT: 0,
    CORNER: 1
};

const MAX_ANGLE = Math.PI / 2;

export default class LabelLine extends Label {

    constructor (size, lines, options) {
        super(size, options);

        this.lines = lines;

        this.segment_size = options.segment_size;

        this.placement = (options.placement === undefined) ? PLACEMENT.MID_POINT : options.placement;

        this.position = null;
        this.pre_offset = [[0,0], [0,0]];
        this.collapsed_size = [];
        this.kink_index = 0;

        // optionally limit the line segments that the label may be placed in, by specifying a segment index range
        // used as a coarse subdivide for placing multiple labels per line geometry
        this.segment_index = options.segment_index || options.segment_start || 0;
        this.segment_max = options.segment_end || this.lines.length;

        this.throw_away = false;

        // get first good segment
        var segment = this.getNextFittingSegment(this.getCurrentSegment());

        if (!segment) this.throw_away = true;
    }

    nextLabelLine() {
        // increment segment
        var hasNext = this.getNextSegment();
        if (!hasNext) return false;

        // clone options
        var options = Object.create(this.options);
        options.segment_index = this.segment_index;
        options.placement = this.placement;

        // create new label
        var label = new LabelLine(this.size, this.lines, options);

        return (label.throw_away) ? false : label;
    }

    getNextSegment() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                this.placement = PLACEMENT.MID_POINT;
                this.kink_index = 0;
                this.pre_offset = [[0, 0], [0, 0]];
                this.collapsed_size = [];
                break;
            case PLACEMENT.MID_POINT:
                if (this.segment_index >= this.lines.length - 2) return false;
                if (this.segment_size.length > 1) {
                    this.placement = PLACEMENT.CORNER;
                }
                this.segment_index++;
                break;
        }

        return this.getCurrentSegment();
    }

    getCurrentSegment() {
        let p1, p2, segment;
        switch (this.placement) {
            case PLACEMENT.CORNER:
                p1 = this.lines[this.segment_index - 1];
                p2 = this.lines[this.segment_index];
                let p3 = this.lines[this.segment_index + 1];
                segment = [ p1, p2, p3 ];
                break;
            case PLACEMENT.MID_POINT:
                p1 = this.lines[this.segment_index];
                p2 = this.lines[this.segment_index + 1];
                segment = [ p1, p2 ];
                break;
        }

        return segment;
    }

    getNextFittingSegment(segment) {
        segment = segment || this.getNextSegment();
        if (!segment) return false;

        if (this.doesSegmentFit(segment)) {
            this.update();
            if (this.inTileBounds() && this.inAngleBounds())
                return segment;
        }

        return this.getNextFittingSegment();
    }

    doesSegmentFit(segment) {
        let does_fit = false;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                does_fit = this.fitKinkedSegment(segment);
                break;
            case PLACEMENT.MID_POINT:
                let excess = 100 / (100 - this.options.line_exceed);
                let p0p1 = Vector.sub(segment[0], segment[1]);
                let line_length = Vector.length(p0p1);

                let label_length = this.options.collision_size[0] * this.options.units_per_pixel;
                does_fit = (label_length < excess * line_length)
                break;
        }

        return does_fit;
    }

    fitKinkedSegment(segment) {
        let excess = 100 / (100 - this.options.line_exceed);
        let opp = this.options.units_per_pixel;

        let does_fit = false;

        let p0p1 = Vector.sub(segment[0], segment[1]);
        let p1p2 = Vector.sub(segment[1], segment[2]);

        let line_length1 = Vector.length(p0p1);
        let line_length2 = Vector.length(p1p2);

        // break up multiple segments into two chunks (N-1 options)
        let label_length1 = this.options.collision_size[0];
        let label_length2 = 0;
        let width;

        this.kink_index = this.segment_size.length - 1;

        while (!does_fit && this.kink_index > 0) {
            width = this.segment_size[this.kink_index];

            label_length1 -= width;
            label_length2 += width;

            does_fit = (opp * label_length1 < excess * line_length1 && opp * label_length2 < excess * line_length2);
            if (!does_fit) this.kink_index--;
        }

        if (does_fit && this.kink_index > 0) {
            var width1 = 0;
            var width2 = this.size[0];
            for (var i = 0; i < this.kink_index; i++) {
                var segment_width = this.segment_size[i];
                width1 += segment_width;
                width2 -= segment_width;
            }
            this.collapsed_size[0] = width1;
            this.collapsed_size[1] = width2;
            return true;
        }
        else return false;
    }

    update() {
        this.angle = this.getCurrentAngle();
        this.position = this.getCurrentPosition();
        this.updateBBoxes();
    }

    getCurrentAngle() {
        var segment = this.getCurrentSegment();
        var angle;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var theta1 = getAngleFromSegment(segment[0], segment[1]);
                var theta2 = getAngleFromSegment(segment[1], segment[2]);

                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                if (orientation1 !== orientation2) {
                    theta2 -= Math.PI;
                }

                angle = (orientation1) ? [theta2, theta1] : [theta1, theta2];
                break;
            case PLACEMENT.MID_POINT:
                var theta = getAngleFromSegment(segment[0], segment[1]);
                angle = [theta];
                break;
        }

        return angle;
    }

    getCurrentPosition() {
        let segment = this.getCurrentSegment();
        let position;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                position = segment[1].slice();
                break;
            case PLACEMENT.MID_POINT:
                position = [
                    (segment[0][0] + segment[1][0]) / 2,
                    (segment[0][1] + segment[1][1]) / 2
                ];
                break;
        }

        return position;
    }

    inAngleBounds() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                var angle0 = this.angle[0];
                if (angle0 < 0) angle0 += 2 * Math.PI;

                var angle1 = this.angle[1];
                if (angle1 < 0) angle1 += 2 * Math.PI;

                var theta = Math.abs(angle1 - angle0);

                theta = Math.min(2 * Math.PI - theta, theta);

                return theta <= MAX_ANGLE;
            case PLACEMENT.MID_POINT:
                return true;
        }
    }

    updateBBoxes() {
        let upp = this.options.units_per_pixel;
        let height = (this.options.collision_size[1] + this.options.buffer[1] * 2) * upp * Label.epsilon;

        this.obbs = [];
        this.aabbs = [];

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var segment = this.getCurrentSegment();
                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                var angle0 = this.angle[0];
                if (angle0 < 0) angle0 += 2 * Math.PI;

                var angle1 = this.angle[1];
                if (angle1 < 0) angle1 += 2 * Math.PI;

                var theta = Math.PI - Math.abs(angle1 - angle0);

                var dx = Math.abs(0.5 * this.options.collision_size[1] / Math.tan(0.5 * theta));

                for (var i = 0; i < 2; i++){
                    var width_px = this.collapsed_size[i];
                    var angle = this.angle[i];

                    let width = width_px * upp * Label.epsilon;

                    var direction = (i == 0) ? -1 : 1;
                    var nudge = direction * (width/2 + dx);
                    var offset = Vector.rot([nudge, 0], -angle);
                    var position = Vector.add(this.position, offset);

                    var obb = getOBB(position, width, height, angle, this.offset, upp);
                    var aabb = obb.getExtent();

                    this.obbs.push(obb);
                    this.aabbs.push(aabb);

                    this.pre_offset[i][0] = direction * (this.collapsed_size[i]/2 + dx);
                }
                break;
            case PLACEMENT.MID_POINT:
                let width = (this.options.collision_size[0] + this.options.buffer[0] * 2) * upp * Label.epsilon;

                var angle = this.angle[0];
                var obb = getOBB(this.position, width, height, angle, this.offset, upp);
                var aabb = obb.getExtent();

                this.obbs.push(obb);
                this.aabbs.push(aabb);
                break;
        }
    }

    inTileBounds() {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obj = { aabb };
            var in_bounds = super.inTileBounds.call(obj);
            if (!in_bounds) return false;
        }
        return true;
    }

    add(bboxes) {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            super.add.call(obj, bboxes);
        }
    }

    discard(bboxes) {
        for (var i = 0; i < this.obbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };

            var shouldDiscard = super.occluded.call(obj, bboxes);
            if (shouldDiscard) return true;
        }
        return false;
    }
}

function getOBB(position, width, height, angle, offset, upp) {
    let p0, p1;
    // apply offset, x positive, y pointing down
    if (offset[0] !== 0 || offset[1] !== 0) {
        offset = Vector.rot(offset, angle);
        p0 = position[0] + (offset[0] * upp);
        p1 = position[1] - (offset[1] * upp);
    }
    else {
        p0 = position[0];
        p1 = position[1];
    }

    // the angle of the obb is negative since it's the tile system y axis is pointing down
    return new OBB(p0, p1, -angle, width, height);
}

function getAngleFromSegment(pt1, pt2) {
    let PI = Math.PI;
    let PI_2 = PI / 2;
    let p1p2 = Vector.sub(pt1, pt2);
    let theta = Math.atan2(p1p2[0], p1p2[1]) + PI_2;

    if (theta >= PI_2) {
        // If in 2nd quadrant, move to 4th quadrant
        theta += PI;
        theta %= 2*Math.PI
    }
    else if (theta < 0) {
        // If in 4th quadrant, make a positive angle
        theta += 2 * PI
    }

    return theta;
}

function getOrientationFromSegment(pt1, pt2) {
    return pt1[0] >= pt2[0];
}
<commit_msg>Add angle property to contructor<commit_after>import Vector from '../vector';
import Label from './label';
import OBB from '../utils/obb';
import Geo from '../geo';

const PLACEMENT = {
    MID_POINT: 0,
    CORNER: 1
};

const MAX_ANGLE = Math.PI / 2;

export default class LabelLine extends Label {

    constructor (size, lines, options) {
        super(size, options);

        this.lines = lines;

        this.segment_size = options.segment_size;

        this.placement = (options.placement === undefined) ? PLACEMENT.MID_POINT : options.placement;

        this.position = null;
        this.pre_offset = [[0,0], [0,0]];
        this.collapsed_size = [];
        this.kink_index = 0;
        this.angle = [];

        // optionally limit the line segments that the label may be placed in, by specifying a segment index range
        // used as a coarse subdivide for placing multiple labels per line geometry
        this.segment_index = options.segment_index || options.segment_start || 0;
        this.segment_max = options.segment_end || this.lines.length;

        this.throw_away = false;

        // get first good segment
        var segment = this.getNextFittingSegment(this.getCurrentSegment());

        if (!segment) this.throw_away = true;
    }

    nextLabelLine() {
        // increment segment
        var hasNext = this.getNextSegment();
        if (!hasNext) return false;

        // clone options
        var options = Object.create(this.options);
        options.segment_index = this.segment_index;
        options.placement = this.placement;

        // create new label
        var label = new LabelLine(this.size, this.lines, options);

        return (label.throw_away) ? false : label;
    }

    getNextSegment() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                this.placement = PLACEMENT.MID_POINT;
                this.kink_index = 0;
                this.pre_offset = [[0, 0], [0, 0]];
                this.collapsed_size = [];
                this.angle = [];
                break;
            case PLACEMENT.MID_POINT:
                if (this.segment_index >= this.lines.length - 2) return false;
                if (this.segment_size.length > 1) {
                    this.placement = PLACEMENT.CORNER;
                }
                this.segment_index++;
                break;
        }

        return this.getCurrentSegment();
    }

    getCurrentSegment() {
        let p1, p2, segment;
        switch (this.placement) {
            case PLACEMENT.CORNER:
                p1 = this.lines[this.segment_index - 1];
                p2 = this.lines[this.segment_index];
                let p3 = this.lines[this.segment_index + 1];
                segment = [ p1, p2, p3 ];
                break;
            case PLACEMENT.MID_POINT:
                p1 = this.lines[this.segment_index];
                p2 = this.lines[this.segment_index + 1];
                segment = [ p1, p2 ];
                break;
        }

        return segment;
    }

    getNextFittingSegment(segment) {
        segment = segment || this.getNextSegment();
        if (!segment) return false;

        if (this.doesSegmentFit(segment)) {
            this.update();
            if (this.inTileBounds() && this.inAngleBounds())
                return segment;
        }

        return this.getNextFittingSegment();
    }

    doesSegmentFit(segment) {
        let does_fit = false;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                does_fit = this.fitKinkedSegment(segment);
                break;
            case PLACEMENT.MID_POINT:
                let excess = 100 / (100 - this.options.line_exceed);
                let p0p1 = Vector.sub(segment[0], segment[1]);
                let line_length = Vector.length(p0p1);

                let label_length = this.options.collision_size[0] * this.options.units_per_pixel;
                does_fit = (label_length < excess * line_length)
                break;
        }

        return does_fit;
    }

    fitKinkedSegment(segment) {
        let excess = 100 / (100 - this.options.line_exceed);
        let opp = this.options.units_per_pixel;

        let does_fit = false;

        let p0p1 = Vector.sub(segment[0], segment[1]);
        let p1p2 = Vector.sub(segment[1], segment[2]);

        let line_length1 = Vector.length(p0p1);
        let line_length2 = Vector.length(p1p2);

        // break up multiple segments into two chunks (N-1 options)
        let label_length1 = this.options.collision_size[0];
        let label_length2 = 0;
        let width;

        this.kink_index = this.segment_size.length - 1;

        while (!does_fit && this.kink_index > 0) {
            width = this.segment_size[this.kink_index];

            label_length1 -= width;
            label_length2 += width;

            does_fit = (opp * label_length1 < excess * line_length1 && opp * label_length2 < excess * line_length2);
            if (!does_fit) this.kink_index--;
        }

        if (does_fit && this.kink_index > 0) {
            var width1 = 0;
            var width2 = this.size[0];
            for (var i = 0; i < this.kink_index; i++) {
                var segment_width = this.segment_size[i];
                width1 += segment_width;
                width2 -= segment_width;
            }
            this.collapsed_size[0] = width1;
            this.collapsed_size[1] = width2;
            return true;
        }
        else return false;
    }

    update() {
        this.angle = this.getCurrentAngle();
        this.position = this.getCurrentPosition();
        this.updateBBoxes();
    }

    getCurrentAngle() {
        var segment = this.getCurrentSegment();
        var angle;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var theta1 = getAngleFromSegment(segment[0], segment[1]);
                var theta2 = getAngleFromSegment(segment[1], segment[2]);

                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                if (orientation1 !== orientation2) {
                    theta2 -= Math.PI;
                }

                angle = (orientation1) ? [theta2, theta1] : [theta1, theta2];
                break;
            case PLACEMENT.MID_POINT:
                var theta = getAngleFromSegment(segment[0], segment[1]);
                angle = [theta];
                break;
        }

        return angle;
    }

    getCurrentPosition() {
        let segment = this.getCurrentSegment();
        let position;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                position = segment[1].slice();
                break;
            case PLACEMENT.MID_POINT:
                position = [
                    (segment[0][0] + segment[1][0]) / 2,
                    (segment[0][1] + segment[1][1]) / 2
                ];
                break;
        }

        return position;
    }

    inAngleBounds() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                var angle0 = this.angle[0];
                if (angle0 < 0) angle0 += 2 * Math.PI;

                var angle1 = this.angle[1];
                if (angle1 < 0) angle1 += 2 * Math.PI;

                var theta = Math.abs(angle1 - angle0);

                theta = Math.min(2 * Math.PI - theta, theta);

                return theta <= MAX_ANGLE;
            case PLACEMENT.MID_POINT:
                return true;
        }
    }

    updateBBoxes() {
        let upp = this.options.units_per_pixel;
        let height = (this.options.collision_size[1] + this.options.buffer[1] * 2) * upp * Label.epsilon;

        this.obbs = [];
        this.aabbs = [];

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var segment = this.getCurrentSegment();
                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                var angle0 = this.angle[0];
                if (angle0 < 0) angle0 += 2 * Math.PI;

                var angle1 = this.angle[1];
                if (angle1 < 0) angle1 += 2 * Math.PI;

                var theta = Math.PI - Math.abs(angle1 - angle0);

                var dx = Math.abs(0.5 * this.options.collision_size[1] / Math.tan(0.5 * theta));

                for (var i = 0; i < 2; i++){
                    var width_px = this.collapsed_size[i];
                    var angle = this.angle[i];

                    let width = width_px * upp * Label.epsilon;

                    var direction = (i == 0) ? -1 : 1;
                    var nudge = direction * (width/2 + dx);
                    var offset = Vector.rot([nudge, 0], -angle);
                    var position = Vector.add(this.position, offset);

                    var obb = getOBB(position, width, height, angle, this.offset, upp);
                    var aabb = obb.getExtent();

                    this.obbs.push(obb);
                    this.aabbs.push(aabb);

                    this.pre_offset[i][0] = direction * (this.collapsed_size[i]/2 + dx);
                }
                break;
            case PLACEMENT.MID_POINT:
                let width = (this.options.collision_size[0] + this.options.buffer[0] * 2) * upp * Label.epsilon;

                var angle = this.angle[0];
                var obb = getOBB(this.position, width, height, angle, this.offset, upp);
                var aabb = obb.getExtent();

                this.obbs.push(obb);
                this.aabbs.push(aabb);
                break;
        }
    }

    inTileBounds() {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obj = { aabb };
            var in_bounds = super.inTileBounds.call(obj);
            if (!in_bounds) return false;
        }
        return true;
    }

    add(bboxes) {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            super.add.call(obj, bboxes);
        }
    }

    discard(bboxes) {
        for (var i = 0; i < this.obbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };

            var shouldDiscard = super.occluded.call(obj, bboxes);
            if (shouldDiscard) return true;
        }
        return false;
    }
}

function getOBB(position, width, height, angle, offset, upp) {
    let p0, p1;
    // apply offset, x positive, y pointing down
    if (offset[0] !== 0 || offset[1] !== 0) {
        offset = Vector.rot(offset, angle);
        p0 = position[0] + (offset[0] * upp);
        p1 = position[1] - (offset[1] * upp);
    }
    else {
        p0 = position[0];
        p1 = position[1];
    }

    // the angle of the obb is negative since it's the tile system y axis is pointing down
    return new OBB(p0, p1, -angle, width, height);
}

function getAngleFromSegment(pt1, pt2) {
    let PI = Math.PI;
    let PI_2 = PI / 2;
    let p1p2 = Vector.sub(pt1, pt2);
    let theta = Math.atan2(p1p2[0], p1p2[1]) + PI_2;

    if (theta >= PI_2) {
        // If in 2nd quadrant, move to 4th quadrant
        theta += PI;
        theta %= 2*Math.PI
    }
    else if (theta < 0) {
        // If in 4th quadrant, make a positive angle
        theta += 2 * PI
    }

    return theta;
}

function getOrientationFromSegment(pt1, pt2) {
    return pt1[0] >= pt2[0];
}
<|endoftext|>


<commit_before>const React = require('react');
const PropTypes = require('prop-types');

import { css } from 'glamor';

import { withTheme } from './Theme';

const Icon = require('./Icon');
const Spin = require('./Spin');

const { buttonTypes, themeShape } = require('../constants/App');

const StyleUtils = require('../utils/Style');
const { deprecatePrimaryColor } = require('../utils/Deprecation');

class Button extends React.Component {
  static propTypes = {
    'aria-label': PropTypes.string,
    actionText: PropTypes.string,
    buttonRef: PropTypes.func,
    className: PropTypes.string,
    elementProps: PropTypes.object,
    icon: PropTypes.string,
    isActive: PropTypes.bool,
    onClick: PropTypes.func,
    primaryColor: PropTypes.string,
    style: PropTypes.object,
    styles: PropTypes.object,
    theme: themeShape,
    type: PropTypes.oneOf(buttonTypes)
  };

  static defaultProps = {
    elementProps: {},
    onClick () {},
    isActive: false,
    type: 'primary'
  };

  componentDidMount () {
    deprecatePrimaryColor(this.props);
  }

  _windowSizeIsSmall = (theme) => {
    const windowSize = StyleUtils.getWindowSize(theme.BreakPoints);

    return !(windowSize === 'medium' || windowSize === 'large');
  };

  _childIsVisible = child =>
    !child.props || child.props.className !== 'visuallyHidden';

  _hasVisibleChildren = () => {
    if (!this.props.children) {
      return false;
    }

    if (!Array.isArray(this.props.children)) {
      return this._childIsVisible(this.props.children);
    }

    return this.props.children.some(this._childIsVisible);
  };

  render () {
    // Manually consume everything that isn't going to be passed down to the button so we don't have to keep adding props one at a time.
    // Keep elementProps for backwards compatibility.
    const { actionText, buttonRef, children, className, elementProps, icon, isActive, primaryColor, style, theme, ...rest } = this.props;
    const mergedTheme = StyleUtils.mergeTheme(theme, primaryColor);
    const styles = this.styles(mergedTheme);

    // We need to remove the styles prop from rest so we don't pass it to children.
    delete rest.styles;

    return (
      <button
        className={'mx-button ' + css({ ...styles.component, ...styles[this.props.type], ...style }) + ' ' + (className || '')}
        disabled={this.props.type === 'disabled'}
        ref={buttonRef}
        {...rest}
        {...elementProps}
      >

          {(icon && !isActive) && (
            <Icon
              size={20}
              style={styles.icon}
              type={icon}
            />
          )}
          {isActive && (
            <Spin direction='counterclockwise'>
              <Icon size={20} type='spinner' />
            </Spin>
          )}

            {isActive ? actionText : children}

      </button>
    );
  }

  styles = (theme) => {
    const windowSizeIsSmall = this._windowSizeIsSmall(theme);

    return {
      component: {
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        borderColor: 'transparent',
        boxSizing: 'border-box',
        display: 'inline-block',
        padding: '4px 14px',
        fontSize: theme.FontSizes.MEDIUM,
        fontFamily: theme.Fonts.SEMIBOLD,
        cursor: this.props.type === 'disabled' ? 'default' : 'pointer',
        transition: 'all .2s ease-in',
        minWidth: 16,
        position: 'relative',
        ...this.props.style
      },
      children: {
        justifyContent: 'center',
        display: 'flex',
        alignItems: 'center',
        lineHeight: '20px'
      },
      primary: {
        backgroundColor: theme.Colors.PRIMARY,
        borderColor: theme.Colors.PRIMARY,
        color: theme.Colors.WHITE,
        fill: theme.Colors.WHITE,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -15),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          transition: 'all .2s ease-in'
        }
      },
      primaryOutline: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.PRIMARY,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: theme.Colors.PRIMARY,
          color: theme.Colors.WHITE,
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          color: theme.Colors.WHITE,
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      primaryInverse: {
        backgroundColor: theme.Colors.WHITE,
        borderColor: theme.Colors.WHITE,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.WHITE, -15),
          borderColor: StyleUtils.adjustColor(theme.Colors.WHITE, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.WHITE, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.WHITE, -30),
          transition: 'all .2s ease-in'
        }
      },
      secondary: {
        backgroundColor: theme.Colors.GRAY_300,
        borderColor: 'transparent',
        color: theme.Colors.GRAY_700,
        fill: theme.Colors.GRAY_500,
        transition: 'all .2s ease-in',
        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_300, -15),
          borderColor: 'transparent',
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_300, -30),
          borderColor: 'transparent',
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      base: {
        backgroundColor: 'transparent',
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',
        borderColor: 'transparent',
        borderRadius: 2,
        borderWidth: 1,
        ':hover': windowSizeIsSmall ? null : {
          color: StyleUtils.adjustColor(theme.Colors.PRIMARY, -8),
          fill: StyleUtils.adjustColor(theme.Colors.PRIMARY, -8),
          transition: 'all .2s ease-in',
          borderColor: theme.Colors.GRAY_300
        },
        ':active': {
          color: StyleUtils.adjustColor(theme.Colors.PRIMARY, -16),
          fill: StyleUtils.adjustColor(theme.Colors.PRIMARY, -16),
          transition: 'all .2s ease-in',
          backgroundColor: theme.Colors.GRAY_100
        }
      },
      neutral: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.GRAY_300,
        borderRadius: 2,
        borderWidth: 1,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: theme.Colors.GRAY_100
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_100, -15)
        }
      },
      disabled: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.GRAY_300,
        color: theme.Colors.GRAY_300,
        fill: theme.Colors.GRAY_300
      },
      icon: {
        marginLeft: this._hasVisibleChildren() ? -4 : 0,
        marginRight: this._hasVisibleChildren() ? 5 : 0
      },
      buttonText: {
        marginLeft: (this.props.isActive && this.props.actionText) ? 10 : 0
      },
      ...this.props.styles
    };
  };
}

module.exports = withTheme(Button);
<commit_msg>Add default styles prop to Button<commit_after>const React = require('react');
const PropTypes = require('prop-types');

import { css } from 'glamor';

import { withTheme } from './Theme';

const Icon = require('./Icon');
const Spin = require('./Spin');

const { buttonTypes, themeShape } = require('../constants/App');

const StyleUtils = require('../utils/Style');
const { deprecatePrimaryColor } = require('../utils/Deprecation');

class Button extends React.Component {
  static propTypes = {
    'aria-label': PropTypes.string,
    actionText: PropTypes.string,
    buttonRef: PropTypes.func,
    className: PropTypes.string,
    elementProps: PropTypes.object,
    icon: PropTypes.string,
    isActive: PropTypes.bool,
    onClick: PropTypes.func,
    primaryColor: PropTypes.string,
    style: PropTypes.object,
    styles: PropTypes.object,
    theme: themeShape,
    type: PropTypes.oneOf(buttonTypes)
  };

  static defaultProps = {
    elementProps: {},
    onClick () {},
    isActive: false,
    styles: {},
    type: 'primary'
  };

  componentDidMount () {
    deprecatePrimaryColor(this.props);
  }

  _windowSizeIsSmall = (theme) => {
    const windowSize = StyleUtils.getWindowSize(theme.BreakPoints);

    return !(windowSize === 'medium' || windowSize === 'large');
  };

  _childIsVisible = child =>
    !child.props || child.props.className !== 'visuallyHidden';

  _hasVisibleChildren = () => {
    if (!this.props.children) {
      return false;
    }

    if (!Array.isArray(this.props.children)) {
      return this._childIsVisible(this.props.children);
    }

    return this.props.children.some(this._childIsVisible);
  };

  render () {
    // Manually consume everything that isn't going to be passed down to the button so we don't have to keep adding props one at a time.
    // Keep elementProps for backwards compatibility.
    const { actionText, buttonRef, children, className, elementProps, icon, isActive, primaryColor, style, theme, ...rest } = this.props;
    const mergedTheme = StyleUtils.mergeTheme(theme, primaryColor);
    const styles = this.styles(mergedTheme);

    // We need to remove the styles prop from rest so we don't pass it to children.
    delete rest.styles;

    return (
      <button
        className={'mx-button ' + css({ ...styles.component, ...styles[this.props.type], ...style }) + ' ' + (className || '')}
        disabled={this.props.type === 'disabled'}
        ref={buttonRef}
        {...rest}
        {...elementProps}
      >

          {(icon && !isActive) && (
            <Icon
              size={20}
              style={styles.icon}
              type={icon}
            />
          )}
          {isActive && (
            <Spin direction='counterclockwise'>
              <Icon size={20} type='spinner' />
            </Spin>
          )}

            {isActive ? actionText : children}

      </button>
    );
  }

  styles = (theme) => {
    const windowSizeIsSmall = this._windowSizeIsSmall(theme);

    return {
      component: {
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        borderColor: 'transparent',
        boxSizing: 'border-box',
        display: 'inline-block',
        padding: '4px 14px',
        fontSize: theme.FontSizes.MEDIUM,
        fontFamily: theme.Fonts.SEMIBOLD,
        cursor: this.props.type === 'disabled' ? 'default' : 'pointer',
        transition: 'all .2s ease-in',
        minWidth: 16,
        position: 'relative',
        ...this.props.style
      },
      children: {
        justifyContent: 'center',
        display: 'flex',
        alignItems: 'center',
        lineHeight: '20px'
      },
      primary: {
        backgroundColor: theme.Colors.PRIMARY,
        borderColor: theme.Colors.PRIMARY,
        color: theme.Colors.WHITE,
        fill: theme.Colors.WHITE,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -15),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          transition: 'all .2s ease-in'
        }
      },
      primaryOutline: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.PRIMARY,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: theme.Colors.PRIMARY,
          color: theme.Colors.WHITE,
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.PRIMARY, -30),
          color: theme.Colors.WHITE,
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      primaryInverse: {
        backgroundColor: theme.Colors.WHITE,
        borderColor: theme.Colors.WHITE,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',

        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.WHITE, -15),
          borderColor: StyleUtils.adjustColor(theme.Colors.WHITE, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.WHITE, -30),
          borderColor: StyleUtils.adjustColor(theme.Colors.WHITE, -30),
          transition: 'all .2s ease-in'
        }
      },
      secondary: {
        backgroundColor: theme.Colors.GRAY_300,
        borderColor: 'transparent',
        color: theme.Colors.GRAY_700,
        fill: theme.Colors.GRAY_500,
        transition: 'all .2s ease-in',
        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_300, -15),
          borderColor: 'transparent',
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_300, -30),
          borderColor: 'transparent',
          fill: theme.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      base: {
        backgroundColor: 'transparent',
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        transition: 'all .2s ease-in',
        borderColor: 'transparent',
        borderRadius: 2,
        borderWidth: 1,
        ':hover': windowSizeIsSmall ? null : {
          color: StyleUtils.adjustColor(theme.Colors.PRIMARY, -8),
          fill: StyleUtils.adjustColor(theme.Colors.PRIMARY, -8),
          transition: 'all .2s ease-in',
          borderColor: theme.Colors.GRAY_300
        },
        ':active': {
          color: StyleUtils.adjustColor(theme.Colors.PRIMARY, -16),
          fill: StyleUtils.adjustColor(theme.Colors.PRIMARY, -16),
          transition: 'all .2s ease-in',
          backgroundColor: theme.Colors.GRAY_100
        }
      },
      neutral: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.GRAY_300,
        borderRadius: 2,
        borderWidth: 1,
        color: theme.Colors.PRIMARY,
        fill: theme.Colors.PRIMARY,
        ':hover': windowSizeIsSmall ? null : {
          backgroundColor: theme.Colors.GRAY_100
        },
        ':active': {
          backgroundColor: StyleUtils.adjustColor(theme.Colors.GRAY_100, -15)
        }
      },
      disabled: {
        backgroundColor: 'transparent',
        borderColor: theme.Colors.GRAY_300,
        color: theme.Colors.GRAY_300,
        fill: theme.Colors.GRAY_300
      },
      icon: {
        marginLeft: this._hasVisibleChildren() ? -4 : 0,
        marginRight: this._hasVisibleChildren() ? 5 : 0
      },
      buttonText: {
        marginLeft: (this.props.isActive && this.props.actionText) ? 10 : 0
      },
      ...this.props.styles
    };
  };
}

module.exports = withTheme(Button);
<|endoftext|>