<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import Input from 'react-input-autosize';
import classNames from 'classnames';

import stripDiacritics from './utils/stripDiacritics';

import MultiValue from './MultiValue';
import Option from './Option';
import SingleValue from './SingleValue';

var requestId = 0;

var Select = React.createClass({

	displayName: 'Select',

	propTypes: {
		addLabelText: React.PropTypes.string,       // placeholder displayed when you want to add a label on a multi-value input
		allowCreate: React.PropTypes.bool,          // whether to allow creation of new entries
		asyncOptions: React.PropTypes.func,         // function to call to get options
		autoload: React.PropTypes.bool,             // whether to auto-load the default async options set
		backspaceRemoves: React.PropTypes.bool,     // whether backspace removes an item if there is no text input
		cacheAsyncResults: React.PropTypes.bool,    // whether to allow cache
		className: React.PropTypes.string,          // className for the outer element
		clearAllText: React.PropTypes.string,       // title for the "clear" control when multi: true
		clearValueText: React.PropTypes.string,     // title for the "clear" control
		clearable: React.PropTypes.bool,            // should it be possible to reset value
		delimiter: React.PropTypes.string,          // delimiter to use to join multiple values
		disabled: React.PropTypes.bool,             // whether the Select is disabled or not
		escapeClearsValue: React.PropTypes.bool,    // whether escape clears the value when the menu is closed
		filterOption: React.PropTypes.func,         // method to filter a single option  (option, filterString)
		filterOptions: React.PropTypes.func,        // method to filter the options array: function ([options], filterString, [values])
		ignoreAccents: React.PropTypes.bool,        // whether to strip diacritics when filtering
		ignoreCase: React.PropTypes.bool,           // whether to perform case-insensitive filtering
		inputProps: React.PropTypes.object,         // custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'}
		isLoading: React.PropTypes.bool,            // whether the Select is loading externally or not (such as options being loaded)
		labelKey: React.PropTypes.string,           // path of the label value in option objects
		matchPos: React.PropTypes.string,           // (any|start) match the start or entire string when filtering
		matchProp: React.PropTypes.string,          // (any|label|value) which option property to filter on
		multi: React.PropTypes.bool,                // multi-value input
		multiValueComponent: React.PropTypes.func,  // value component to render when multiple is set to true
		name: React.PropTypes.string,               // field name, for hidden <input /> tag
		newOptionCreator: React.PropTypes.func,     // factory to create new options when allowCreate set
		noResultsText: React.PropTypes.string,      // placeholder displayed when there are no matching search results
		onBlur: React.PropTypes.func,               // onBlur handler: function (event) {}
		onChange: React.PropTypes.func,             // onChange handler: function (newValue) {}
		onFocus: React.PropTypes.func,              // onFocus handler: function (event) {}
		onInputChange: React.PropTypes.func,        // onInputChange handler: function (inputValue) {}
		onOptionLabelClick: React.PropTypes.func,   // onClick handler for value labels: function (value, event) {}
		optionComponent: React.PropTypes.func,      // option component to render in dropdown
		optionRenderer: React.PropTypes.func,       // optionRenderer: function (option) {}
		options: React.PropTypes.array,             // array of options
		placeholder: React.PropTypes.string,        // field placeholder, displayed when there's no value
		searchable: React.PropTypes.bool,           // whether to enable searching feature or not
		searchingText: React.PropTypes.string,      // message to display whilst options are loading via asyncOptions
		searchPromptText: React.PropTypes.string,   // label to prompt for search input
		singleValueComponent: React.PropTypes.func, // value component to render when multiple is set to false
		value: React.PropTypes.any,                 // initial field value
		valueKey: React.PropTypes.string,           // path of the label value in option objects
		valueRenderer: React.PropTypes.func         // valueRenderer: function (option) {}
	},

	getDefaultProps () {
		return {
			addLabelText: 'Add "{label}"?',
			allowCreate: false,
			autoload: true,
			backspaceRemoves: true,
			cacheAsyncResults: true,
			clearAllText: 'Clear all',
			clearValueText: 'Clear value',
			clearable: true,
			delimiter: ',',
			disabled: false,
			escapeClearsValue: true,
			ignoreAccents: true,
			ignoreCase: true,
			inputProps: {},
			isLoading: false,
			labelKey: 'label',
			matchPos: 'any',
			matchProp: 'any',
			multi: false,
			multiValueComponent: MultiValue,
			noResultsText: 'No results found',
			optionComponent: Option,
			placeholder: 'Select...',
			searchable: true,
			searchingText: 'Searching...',
			searchPromptText: 'Type to search',
			singleValueComponent: SingleValue,
			valueKey: 'value',
		};
	},

	getInitialState () {
		return {
			inputValue: '',
			isFocused: false,
			isLoading: false,
			isOpen: false,
			isPseudoFocused: false,
		};
	},

	componentDidUpdate () {
		if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) {
			this._scrollToFocusedOptionOnUpdate = false;
			var focusedDOM = ReactDOM.findDOMNode(this.refs.focused);
			var menuDOM = ReactDOM.findDOMNode(this.refs.menu);
			var focusedRect = focusedDOM.getBoundingClientRect();
			var menuRect = menuDOM.getBoundingClientRect();
			if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) {
				menuDOM.scrollTop = (focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight);
			}
		}
	},

	focus () {
		if (!this.refs.input) return;
		this.refs.input.focus();
	},

	handleMouseDown (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, or if the component is disabled, ignore it.
		if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
			return;
		}

		// prevent default event handlers
		event.stopPropagation();
		event.preventDefault();

		// for the non-searchable select, close the dropdown when button is clicked
		if (this.state.isOpen && !this.props.searchable) {
			this.setState({
				isOpen: false,
			});
			return;
		}

		if (this.state.isFocused) {
			// if the input is focused, ensure the menu is open
			this.setState({
				isOpen: true,
				isPseudoFocused: false,
			});
		} else {
			// otherwise, focus the input and open the menu
			this._openAfterFocus = true;
			this.focus();
		}
	},

	handleMouseDownOnArrow (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, or if the component is disabled, ignore it.
		if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
			return;
		}
		// If the menu isn't open, let the event bubble to the main handleMouseDown
		if (!this.state.isOpen) {
			return;
		}
		// prevent default event handlers
		event.stopPropagation();
		event.preventDefault();
		// close the menu
		this.closeMenu();
	},

	closeMenu () {
		this.setState({
			isOpen: false,
			isPseudoFocused: this.state.isFocused && !this.props.multi
		});
	},

	handleInputFocus (event) {
		var isOpen = this.state.isOpen || this._openAfterFocus;
		if (this.props.onFocus) {
			this.props.onFocus(event);
		}
		this.setState({
			isFocused: true,
			isOpen: isOpen
		});
		this._openAfterFocus = false;
	},

	handleInputBlur (event) {
		if (document.activeElement.isEqualNode(this.refs.menu)) {
			return;
		}
		if (this.props.onBlur) {
			this.props.onBlur(event);
		}
		this.setState({
			inputValue: '',
			isFocused: false,
			isOpen: false,
			isPseudoFocused: false,
		});
	},

	handleInputChange (event) {
		if (this.props.onInputChange) {
			this.props.onInputChange(event.target.value);
		}
		if (this.props.asyncOptions) {
			this.setState({
				// isLoading: true,
				isPseudoFocused: false,
				inputValue: event.target.value
			});
			// this.loadAsyncOptions(event.target.value, {
			// 	isLoading: false,
			// 	isOpen: true
			// }, this._bindCloseMenuIfClickedOutside);
		} else {
			// var filteredOptions = this.filterOptions(this.state.options);
			this.setState({
				isOpen: true,
				isPseudoFocused: false,
				inputValue: event.target.value,
				// filteredOptions: filteredOptions,
				// focusedOption: this._getNewFocusedOption(filteredOptions)
			});
		}
	},

	handleKeyDown (event) {
		if (this.props.disabled) return;
		switch (event.keyCode) {
			case 8: // backspace
				if (!this.state.inputValue && this.props.backspaceRemoves) {
					event.preventDefault();
					this.popValue();
				}
			return;
			case 9: // tab
				if (event.shiftKey || !this.state.isOpen || !this.state.focusedOption) {
					return;
				}
				this.selectFocusedOption();
			break;
			case 13: // enter
				if (!this.state.isOpen) return;
				this.selectFocusedOption();
			break;
			case 27: // escape
				if (this.state.isOpen) {
					this.closeMenu();
				} else if (this.props.clearable && this.props.escapeClearsValue) {
					this.clearValue(event);
				}
			break;
			case 38: // up
				this.focusPreviousOption();
			break;
			case 40: // down
				this.focusNextOption();
			break;
			// case 188: // ,
			// 	if (this.props.allowCreate && this.props.multi) {
			// 		event.preventDefault();
			// 		event.stopPropagation();
			// 		this.selectFocusedOption();
			// 	} else {
			// 		return;
			// 	}
			// break;
			default: return;
		}
		event.preventDefault();
	},

	getOptionLabel (op) {
		return op[this.props.labelKey];
	},

	getValueArray () {
		if (this.props.multi) {
			if (!Array.isArray(this.props.value)) return [];
			return this.props.value.map(this.expandValue).filter(i => i);
		}
		var expandedValue = this.expandValue(this.props.value);
		return expandedValue ? [expandedValue] : [];
	},

	expandValue (value) {
		if (typeof value !== 'string') return value;
		if (!this.props.options) return;
		for (var i = 0; i < this.props.options.length; i++) {
			if (this.props.options[i][this.props.valueKey] === value) return this.props.options[i];
		}
	},

	setValue (value) {
		if (this.props.onChange) {
			this.props.onChange(value);
		}
	},

	selectValue (value) {
		if (this.props.multi) {
			this.addValue(value);
			this.setState({
				inputValue: '',
			});
		} else {
			this.setValue(value);
			this.setState({
				isOpen: false,
				inputValue: '',
				isPseudoFocused: this.state.isFocused,
			});
		}
	},

	addValue (value) {
		var valueArray = this.getValueArray();
		this.setValue(valueArray.concat(value));
	},

	popValue () {
		var valueArray = this.getValueArray();
		if (!valueArray.length) return;
		this.setValue(valueArray.slice(0, valueArray.length - 1));
	},

	removeValue (value) {
		var valueArray = this.getValueArray();
		this.setValue(valueArray.filter(i => i !== value));
		this.focus();
	},

	clearValue (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, ignore it.
		if (event && event.type === 'mousedown' && event.button !== 0) {
			return;
		}
		event.stopPropagation();
		event.preventDefault();
		this.setValue(null);
		this.setState({
			isOpen: false,
			inputValue: '',
		}, this.focus);
	},

	focusOption (option) {
		this.setState({
			focusedOption: option
		});
	},

	unfocusOption (option) {
		if (this.state.focusedOption === option) {
			this.setState({
				focusedOption: null
			});
		}
	},

	focusNextOption () {
		this.focusAdjacentOption('next');
	},

	focusPreviousOption () {
		this.focusAdjacentOption('previous');
	},

	focusAdjacentOption (dir) {
		var options = this._visibleOptions.filter(i => !i.disabled);
		this._scrollToFocusedOptionOnUpdate = true;
		if (!this.state.isOpen) {
			this.setState({
				isOpen: true,
				inputValue: '',
				focusedOption: this.state.focusedOption || options[dir === 'next' ? 0 : options.length - 1]
			});
			return;
		}
		if (!options.length) return;
		var focusedIndex = -1;
		for (var i = 0; i < options.length; i++) {
			if (this.state.focusedOption === options[i]) {
				focusedIndex = i;
				break;
			}
		}
		var focusedOption = options[0];
		if (dir === 'next' && focusedIndex > -1 && focusedIndex < options.length - 1) {
			focusedOption = options[focusedIndex + 1];
		} else if (dir === 'previous') {
			if (focusedIndex > 0) {
				focusedOption = options[focusedIndex - 1];
			} else {
				focusedOption = options[options.length - 1];
			}
		}
		this.setState({
			focusedOption: focusedOption
		});
	},

	selectFocusedOption () {
		// if (this.props.allowCreate && !this.state.focusedOption) {
		// 	return this.selectValue(this.state.inputValue);
		// }
		if (this.state.focusedOption) {
			return this.selectValue(this.state.focusedOption);
		}
	},

	isLoading () {
		return this.props.isLoading || this.state.isLoading;
	},

	renderLoading () {
		if (!this.isLoading()) return;
		return (
			<span className="Select-loading-zone" aria-hidden="true">
				<span className="Select-loading" />
			</span>
		);
	},

	renderValue (valueArray) {
		var renderLabel = this.props.valueRenderer || this.getOptionLabel;
		var MultiValueComponent = this.props.multiValueComponent;
		var SingleValueComponent = this.props.singleValueComponent;
		if (!valueArray.length) {
			return !this.state.inputValue ? <div className="Select-placeholder">{this.props.placeholder}</div> : null;
		}
		if (this.props.multi) {
			return valueArray.map(i => {
				return (
					<MultiValueComponent
						key={i[this.props.valueKey]}
						value={i}
						onRemove={this.removeValue}
						disabled={this.props.disabled}
						>
						{renderLabel(i)}
					</MultiValueComponent>
				);
			});
		} else if (!this.state.inputValue) {
			return (
				<SingleValueComponent
					value={valueArray[0]}
					>
					{renderLabel(valueArray[0])}
				</SingleValueComponent>
			);
		}
	},

	renderInput (valueArray) {
		var className = classNames('Select-input', this.props.inputProps.className);
		if (this.props.disabled || !this.props.searchable) {
			if (this.props.multi && valueArray.length) return;
			return <div className={className}>&nbsp;</div>;
		}
		return (
			<Input
				{...this.props.inputProps}
				className={className}
				tabIndex={this.props.tabIndex}
				onBlur={this.handleInputBlur}
				onChange={this.handleInputChange}
				onFocus={this.handleInputFocus}
				minWidth="5"
				ref="input"
				value={this.state.inputValue}
			/>
		);
	},

	renderClear () {
		if (!this.props.clearable || !this.props.value || (this.props.multi && !this.props.value.length) || this.props.disabled || this.isLoading()) return;
		return (
			<span className="Select-clear-zone" title={this.props.multi ? this.props.clearAllText : this.props.clearValueText} aria-label={this.props.multi ? this.props.clearAllText : this.props.clearValueText} onMouseDown={this.clearValue} onTouchEnd={this.clearValue}>
				<span className="Select-clear" dangerouslySetInnerHTML={{ __html: '&times;' }} />
			</span>
		);
	},

	renderArrow () {
		return (
			<span className="Select-arrow-zone" onMouseDown={this.handleMouseDownOnArrow}>
				<span className="Select-arrow" onMouseDown={this.handleMouseDownOnArrow} />
			</span>
		);
	},

	filterOptions (excludeOptions) {
		var filterValue = this.state.inputValue;
		var options = this.props.options || [];
		if (this.props.filterOptions) {
			return this.props.filterOptions.call(this, options, filterValue, excludeOptions);
		} else {
			if (this.props.ignoreCase) {
				filterValue = filterValue.toLowerCase();
			}
			if (this.props.ignoreAccents) {
				filterValue = stripDiacritics(filterValue);
			}
			if (excludeOptions) excludeOptions = excludeOptions.map(i => i[this.props.valueKey]);
			return options.filter(option => {
				if (excludeOptions && excludeOptions.indexOf(option[this.props.valueKey]) > -1) return false;
				if (this.props.filterOption) return this.props.filterOption.call(this, option, filterValue);
				if (!filterValue) return true;
				var valueTest = String(option[this.props.valueKey]);
				var labelTest = String(option[this.props.labelKey]);
				if (this.props.ignoreCase) {
					if (this.props.matchProp !== 'label') valueTest = valueTest.toLowerCase();
					if (this.props.matchProp !== 'value') labelTest = labelTest.toLowerCase();
				}
				if (this.props.ignoreAccents) {
					if (this.props.matchProp !== 'label') valueTest = stripDiacritics(valueTest);
					if (this.props.matchProp !== 'value') labelTest = stripDiacritics(labelTest);
				}
				return this.props.matchPos === 'start' ? (
					(this.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue) ||
					(this.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue)
				) : (
					(this.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0) ||
					(this.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0)
				);
			});
		}
	},

	renderMenuOptions (options, selected) {
		if (options && options.length) {
			var Option = this.props.optionComponent;
			var renderLabel = this.props.optionRenderer || this.getOptionLabel;
			return options.map(option => {
				var isSelected = selected.indexOf(option) > -1;
				var isFocused = option === this.state.focusedOption;
				var optionRef = isFocused ? 'focused' : null;
				var optionClass = classNames({
					'Select-option': true,
					'is-selected': isSelected,<commit_msg>Diacritics are case-sensitive so ignore accents before case<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import Input from 'react-input-autosize';
import classNames from 'classnames';

import stripDiacritics from './utils/stripDiacritics';

import MultiValue from './MultiValue';
import Option from './Option';
import SingleValue from './SingleValue';

var requestId = 0;

var Select = React.createClass({

	displayName: 'Select',

	propTypes: {
		addLabelText: React.PropTypes.string,       // placeholder displayed when you want to add a label on a multi-value input
		allowCreate: React.PropTypes.bool,          // whether to allow creation of new entries
		asyncOptions: React.PropTypes.func,         // function to call to get options
		autoload: React.PropTypes.bool,             // whether to auto-load the default async options set
		backspaceRemoves: React.PropTypes.bool,     // whether backspace removes an item if there is no text input
		cacheAsyncResults: React.PropTypes.bool,    // whether to allow cache
		className: React.PropTypes.string,          // className for the outer element
		clearAllText: React.PropTypes.string,       // title for the "clear" control when multi: true
		clearValueText: React.PropTypes.string,     // title for the "clear" control
		clearable: React.PropTypes.bool,            // should it be possible to reset value
		delimiter: React.PropTypes.string,          // delimiter to use to join multiple values
		disabled: React.PropTypes.bool,             // whether the Select is disabled or not
		escapeClearsValue: React.PropTypes.bool,    // whether escape clears the value when the menu is closed
		filterOption: React.PropTypes.func,         // method to filter a single option  (option, filterString)
		filterOptions: React.PropTypes.func,        // method to filter the options array: function ([options], filterString, [values])
		ignoreAccents: React.PropTypes.bool,        // whether to strip diacritics when filtering
		ignoreCase: React.PropTypes.bool,           // whether to perform case-insensitive filtering
		inputProps: React.PropTypes.object,         // custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'}
		isLoading: React.PropTypes.bool,            // whether the Select is loading externally or not (such as options being loaded)
		labelKey: React.PropTypes.string,           // path of the label value in option objects
		matchPos: React.PropTypes.string,           // (any|start) match the start or entire string when filtering
		matchProp: React.PropTypes.string,          // (any|label|value) which option property to filter on
		multi: React.PropTypes.bool,                // multi-value input
		multiValueComponent: React.PropTypes.func,  // value component to render when multiple is set to true
		name: React.PropTypes.string,               // field name, for hidden <input /> tag
		newOptionCreator: React.PropTypes.func,     // factory to create new options when allowCreate set
		noResultsText: React.PropTypes.string,      // placeholder displayed when there are no matching search results
		onBlur: React.PropTypes.func,               // onBlur handler: function (event) {}
		onChange: React.PropTypes.func,             // onChange handler: function (newValue) {}
		onFocus: React.PropTypes.func,              // onFocus handler: function (event) {}
		onInputChange: React.PropTypes.func,        // onInputChange handler: function (inputValue) {}
		onOptionLabelClick: React.PropTypes.func,   // onClick handler for value labels: function (value, event) {}
		optionComponent: React.PropTypes.func,      // option component to render in dropdown
		optionRenderer: React.PropTypes.func,       // optionRenderer: function (option) {}
		options: React.PropTypes.array,             // array of options
		placeholder: React.PropTypes.string,        // field placeholder, displayed when there's no value
		searchable: React.PropTypes.bool,           // whether to enable searching feature or not
		searchingText: React.PropTypes.string,      // message to display whilst options are loading via asyncOptions
		searchPromptText: React.PropTypes.string,   // label to prompt for search input
		singleValueComponent: React.PropTypes.func, // value component to render when multiple is set to false
		value: React.PropTypes.any,                 // initial field value
		valueKey: React.PropTypes.string,           // path of the label value in option objects
		valueRenderer: React.PropTypes.func         // valueRenderer: function (option) {}
	},

	getDefaultProps () {
		return {
			addLabelText: 'Add "{label}"?',
			allowCreate: false,
			autoload: true,
			backspaceRemoves: true,
			cacheAsyncResults: true,
			clearAllText: 'Clear all',
			clearValueText: 'Clear value',
			clearable: true,
			delimiter: ',',
			disabled: false,
			escapeClearsValue: true,
			ignoreAccents: true,
			ignoreCase: true,
			inputProps: {},
			isLoading: false,
			labelKey: 'label',
			matchPos: 'any',
			matchProp: 'any',
			multi: false,
			multiValueComponent: MultiValue,
			noResultsText: 'No results found',
			optionComponent: Option,
			placeholder: 'Select...',
			searchable: true,
			searchingText: 'Searching...',
			searchPromptText: 'Type to search',
			singleValueComponent: SingleValue,
			valueKey: 'value',
		};
	},

	getInitialState () {
		return {
			inputValue: '',
			isFocused: false,
			isLoading: false,
			isOpen: false,
			isPseudoFocused: false,
		};
	},

	componentDidUpdate () {
		if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) {
			this._scrollToFocusedOptionOnUpdate = false;
			var focusedDOM = ReactDOM.findDOMNode(this.refs.focused);
			var menuDOM = ReactDOM.findDOMNode(this.refs.menu);
			var focusedRect = focusedDOM.getBoundingClientRect();
			var menuRect = menuDOM.getBoundingClientRect();
			if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) {
				menuDOM.scrollTop = (focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight);
			}
		}
	},

	focus () {
		if (!this.refs.input) return;
		this.refs.input.focus();
	},

	handleMouseDown (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, or if the component is disabled, ignore it.
		if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
			return;
		}

		// prevent default event handlers
		event.stopPropagation();
		event.preventDefault();

		// for the non-searchable select, close the dropdown when button is clicked
		if (this.state.isOpen && !this.props.searchable) {
			this.setState({
				isOpen: false,
			});
			return;
		}

		if (this.state.isFocused) {
			// if the input is focused, ensure the menu is open
			this.setState({
				isOpen: true,
				isPseudoFocused: false,
			});
		} else {
			// otherwise, focus the input and open the menu
			this._openAfterFocus = true;
			this.focus();
		}
	},

	handleMouseDownOnArrow (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, or if the component is disabled, ignore it.
		if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
			return;
		}
		// If the menu isn't open, let the event bubble to the main handleMouseDown
		if (!this.state.isOpen) {
			return;
		}
		// prevent default event handlers
		event.stopPropagation();
		event.preventDefault();
		// close the menu
		this.closeMenu();
	},

	closeMenu () {
		this.setState({
			isOpen: false,
			isPseudoFocused: this.state.isFocused && !this.props.multi
		});
	},

	handleInputFocus (event) {
		var isOpen = this.state.isOpen || this._openAfterFocus;
		if (this.props.onFocus) {
			this.props.onFocus(event);
		}
		this.setState({
			isFocused: true,
			isOpen: isOpen
		});
		this._openAfterFocus = false;
	},

	handleInputBlur (event) {
		if (document.activeElement.isEqualNode(this.refs.menu)) {
			return;
		}
		if (this.props.onBlur) {
			this.props.onBlur(event);
		}
		this.setState({
			inputValue: '',
			isFocused: false,
			isOpen: false,
			isPseudoFocused: false,
		});
	},

	handleInputChange (event) {
		if (this.props.onInputChange) {
			this.props.onInputChange(event.target.value);
		}
		if (this.props.asyncOptions) {
			this.setState({
				// isLoading: true,
				isPseudoFocused: false,
				inputValue: event.target.value
			});
			// this.loadAsyncOptions(event.target.value, {
			// 	isLoading: false,
			// 	isOpen: true
			// }, this._bindCloseMenuIfClickedOutside);
		} else {
			// var filteredOptions = this.filterOptions(this.state.options);
			this.setState({
				isOpen: true,
				isPseudoFocused: false,
				inputValue: event.target.value,
				// filteredOptions: filteredOptions,
				// focusedOption: this._getNewFocusedOption(filteredOptions)
			});
		}
	},

	handleKeyDown (event) {
		if (this.props.disabled) return;
		switch (event.keyCode) {
			case 8: // backspace
				if (!this.state.inputValue && this.props.backspaceRemoves) {
					event.preventDefault();
					this.popValue();
				}
			return;
			case 9: // tab
				if (event.shiftKey || !this.state.isOpen || !this.state.focusedOption) {
					return;
				}
				this.selectFocusedOption();
			break;
			case 13: // enter
				if (!this.state.isOpen) return;
				this.selectFocusedOption();
			break;
			case 27: // escape
				if (this.state.isOpen) {
					this.closeMenu();
				} else if (this.props.clearable && this.props.escapeClearsValue) {
					this.clearValue(event);
				}
			break;
			case 38: // up
				this.focusPreviousOption();
			break;
			case 40: // down
				this.focusNextOption();
			break;
			// case 188: // ,
			// 	if (this.props.allowCreate && this.props.multi) {
			// 		event.preventDefault();
			// 		event.stopPropagation();
			// 		this.selectFocusedOption();
			// 	} else {
			// 		return;
			// 	}
			// break;
			default: return;
		}
		event.preventDefault();
	},

	getOptionLabel (op) {
		return op[this.props.labelKey];
	},

	getValueArray () {
		if (this.props.multi) {
			if (!Array.isArray(this.props.value)) return [];
			return this.props.value.map(this.expandValue).filter(i => i);
		}
		var expandedValue = this.expandValue(this.props.value);
		return expandedValue ? [expandedValue] : [];
	},

	expandValue (value) {
		if (typeof value !== 'string') return value;
		if (!this.props.options) return;
		for (var i = 0; i < this.props.options.length; i++) {
			if (this.props.options[i][this.props.valueKey] === value) return this.props.options[i];
		}
	},

	setValue (value) {
		if (this.props.onChange) {
			this.props.onChange(value);
		}
	},

	selectValue (value) {
		if (this.props.multi) {
			this.addValue(value);
			this.setState({
				inputValue: '',
			});
		} else {
			this.setValue(value);
			this.setState({
				isOpen: false,
				inputValue: '',
				isPseudoFocused: this.state.isFocused,
			});
		}
	},

	addValue (value) {
		var valueArray = this.getValueArray();
		this.setValue(valueArray.concat(value));
	},

	popValue () {
		var valueArray = this.getValueArray();
		if (!valueArray.length) return;
		this.setValue(valueArray.slice(0, valueArray.length - 1));
	},

	removeValue (value) {
		var valueArray = this.getValueArray();
		this.setValue(valueArray.filter(i => i !== value));
		this.focus();
	},

	clearValue (event) {
		// if the event was triggered by a mousedown and not the primary
		// button, ignore it.
		if (event && event.type === 'mousedown' && event.button !== 0) {
			return;
		}
		event.stopPropagation();
		event.preventDefault();
		this.setValue(null);
		this.setState({
			isOpen: false,
			inputValue: '',
		}, this.focus);
	},

	focusOption (option) {
		this.setState({
			focusedOption: option
		});
	},

	unfocusOption (option) {
		if (this.state.focusedOption === option) {
			this.setState({
				focusedOption: null
			});
		}
	},

	focusNextOption () {
		this.focusAdjacentOption('next');
	},

	focusPreviousOption () {
		this.focusAdjacentOption('previous');
	},

	focusAdjacentOption (dir) {
		var options = this._visibleOptions.filter(i => !i.disabled);
		this._scrollToFocusedOptionOnUpdate = true;
		if (!this.state.isOpen) {
			this.setState({
				isOpen: true,
				inputValue: '',
				focusedOption: this.state.focusedOption || options[dir === 'next' ? 0 : options.length - 1]
			});
			return;
		}
		if (!options.length) return;
		var focusedIndex = -1;
		for (var i = 0; i < options.length; i++) {
			if (this.state.focusedOption === options[i]) {
				focusedIndex = i;
				break;
			}
		}
		var focusedOption = options[0];
		if (dir === 'next' && focusedIndex > -1 && focusedIndex < options.length - 1) {
			focusedOption = options[focusedIndex + 1];
		} else if (dir === 'previous') {
			if (focusedIndex > 0) {
				focusedOption = options[focusedIndex - 1];
			} else {
				focusedOption = options[options.length - 1];
			}
		}
		this.setState({
			focusedOption: focusedOption
		});
	},

	selectFocusedOption () {
		// if (this.props.allowCreate && !this.state.focusedOption) {
		// 	return this.selectValue(this.state.inputValue);
		// }
		if (this.state.focusedOption) {
			return this.selectValue(this.state.focusedOption);
		}
	},

	isLoading () {
		return this.props.isLoading || this.state.isLoading;
	},

	renderLoading () {
		if (!this.isLoading()) return;
		return (
			<span className="Select-loading-zone" aria-hidden="true">
				<span className="Select-loading" />
			</span>
		);
	},

	renderValue (valueArray) {
		var renderLabel = this.props.valueRenderer || this.getOptionLabel;
		var MultiValueComponent = this.props.multiValueComponent;
		var SingleValueComponent = this.props.singleValueComponent;
		if (!valueArray.length) {
			return !this.state.inputValue ? <div className="Select-placeholder">{this.props.placeholder}</div> : null;
		}
		if (this.props.multi) {
			return valueArray.map(i => {
				return (
					<MultiValueComponent
						key={i[this.props.valueKey]}
						value={i}
						onRemove={this.removeValue}
						disabled={this.props.disabled}
						>
						{renderLabel(i)}
					</MultiValueComponent>
				);
			});
		} else if (!this.state.inputValue) {
			return (
				<SingleValueComponent
					value={valueArray[0]}
					>
					{renderLabel(valueArray[0])}
				</SingleValueComponent>
			);
		}
	},

	renderInput (valueArray) {
		var className = classNames('Select-input', this.props.inputProps.className);
		if (this.props.disabled || !this.props.searchable) {
			if (this.props.multi && valueArray.length) return;
			return <div className={className}>&nbsp;</div>;
		}
		return (
			<Input
				{...this.props.inputProps}
				className={className}
				tabIndex={this.props.tabIndex}
				onBlur={this.handleInputBlur}
				onChange={this.handleInputChange}
				onFocus={this.handleInputFocus}
				minWidth="5"
				ref="input"
				value={this.state.inputValue}
			/>
		);
	},

	renderClear () {
		if (!this.props.clearable || !this.props.value || (this.props.multi && !this.props.value.length) || this.props.disabled || this.isLoading()) return;
		return (
			<span className="Select-clear-zone" title={this.props.multi ? this.props.clearAllText : this.props.clearValueText} aria-label={this.props.multi ? this.props.clearAllText : this.props.clearValueText} onMouseDown={this.clearValue} onTouchEnd={this.clearValue}>
				<span className="Select-clear" dangerouslySetInnerHTML={{ __html: '&times;' }} />
			</span>
		);
	},

	renderArrow () {
		return (
			<span className="Select-arrow-zone" onMouseDown={this.handleMouseDownOnArrow}>
				<span className="Select-arrow" onMouseDown={this.handleMouseDownOnArrow} />
			</span>
		);
	},

	filterOptions (excludeOptions) {
		var filterValue = this.state.inputValue;
		var options = this.props.options || [];
		if (this.props.filterOptions) {
			return this.props.filterOptions.call(this, options, filterValue, excludeOptions);
		} else {
			if (this.props.ignoreAccents) {
				filterValue = stripDiacritics(filterValue);
			}
			if (this.props.ignoreCase) {
				filterValue = filterValue.toLowerCase();
			}
			if (excludeOptions) excludeOptions = excludeOptions.map(i => i[this.props.valueKey]);
			return options.filter(option => {
				if (excludeOptions && excludeOptions.indexOf(option[this.props.valueKey]) > -1) return false;
				if (this.props.filterOption) return this.props.filterOption.call(this, option, filterValue);
				if (!filterValue) return true;
				var valueTest = String(option[this.props.valueKey]);
				var labelTest = String(option[this.props.labelKey]);
				if (this.props.ignoreAccents) {
					if (this.props.matchProp !== 'label') valueTest = stripDiacritics(valueTest);
					if (this.props.matchProp !== 'value') labelTest = stripDiacritics(labelTest);
				}
				if (this.props.ignoreCase) {
					if (this.props.matchProp !== 'label') valueTest = valueTest.toLowerCase();
					if (this.props.matchProp !== 'value') labelTest = labelTest.toLowerCase();
				}
				return this.props.matchPos === 'start' ? (
					(this.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue) ||
					(this.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue)
				) : (
					(this.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0) ||
					(this.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0)
				);
			});
		}
	},

	renderMenuOptions (options, selected) {
		if (options && options.length) {
			var Option = this.props.optionComponent;
			var renderLabel = this.props.optionRenderer || this.getOptionLabel;
			return options.map(option => {
				var isSelected = selected.indexOf(option) > -1;
				var isFocused = option === this.state.focusedOption;
				var optionRef = isFocused ? 'focused' : null;
				var optionClass = classNames({
					'Select-option': true,
					'is-selected': isSelected,
{'fraction_non_alphanumeric': 0.13441434352517986, 'fraction_numerical': 0.0022482014388489208, 'mean_word_length': 3.279615153337342, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3773112', 'n_tokens_mistral': 12783, 'n_tokens_neox': 11011, 'n_words': 2572}