content: import React from 'react';
  
  import Select from './Select';
  import stripDiacritics from './utils/stripDiacritics';
  
  let requestId = 0;
  
  function initCache (cache) {
  	if (cache && typeof cache !== 'object') {
  		cache = {};
  	}
  	return cache ? cache : null;
  }
  
  function updateCache (cache, input, data) {
  	if (!cache) return;
  	cache[input] = data;
  }
  
  function getFromCache (cache, input) {
  	if (!cache) return;
  	for (let i = input.length; i >= 0; --i) {
  		let cacheKey = input.slice(0, i);
  		if (cache[cacheKey] && (input === cacheKey || cache[cacheKey].complete)) {
  			return cache[cacheKey];
  		}
  	}
  }
  
  function thenPromise (promise, callback) {
  	if (!promise || typeof promise.then !== 'function') return;
  	return promise.then((data) => {
  		callback(null, data);
  	}, (err) => {
  		callback(err);
  	});
  }
  
  const Async = React.createClass({
  	propTypes: {
  		cache: React.PropTypes.any,                     // object to use to cache results, can be null to disable cache
  		loadOptions: React.PropTypes.func.isRequired,   // function to call to load options asynchronously
  		ignoreAccents: React.PropTypes.bool,            // whether to strip diacritics when filtering (shared with Select)
  		ignoreCase: React.PropTypes.bool,               // whether to perform case-insensitive filtering (shared with Select)
  		isLoading: React.PropTypes.bool,                // overrides the isLoading state when set to true
  		loadingPlaceholder: React.PropTypes.string,     // replaces the placeholder while options are loading
  		minimumInput: React.PropTypes.number,           // the minimum number of characters that trigger loadOptions
  		noResultsText: React.PropTypes.string,          // placeholder displayed when there are no matching search results (shared with Select)
  		placeholder: React.PropTypes.oneOfType([        // field placeholder, displayed when there's no value (shared with Select)
  			React.PropTypes.string,
  			React.PropTypes.node
  		]),
  		searchingText: React.PropTypes.string,          // message to display while options are loading
  		searchPromptText: React.PropTypes.string,       // label to prompt for search input
  	},
  	getDefaultProps () {
  		return {
  			cache: true,
  			ignoreAccents: true,
  			ignoreCase: true,
  			loadingPlaceholder: 'Loading...',
  			minimumInput: 1,
  			searchingText: 'Searching...',
  			searchPromptText: 'Type to search',
  		};
  	},
  	getInitialState () {
  		return {
  			cache: initCache(this.props.cache),
  			isLoading: false,
  			options: this.props.options || [],
  		};
  	},
  	componentWillMount () {
  		this._lastInput = '';
  	},
  	componentDidMount () {
  		this.loadOptions('');
  	},
  	componentWillReceiveProps (nextProps) {
  
  		if (nextProps.cache !== this.props.cache) {
  			this.setState({
  				cache: initCache(nextProps.cache),
  			});
  		}
  
  		if( ! this.props.options && nextProps.options) {
  			this.setState({
  				options: nextProps.options
  			});
  		} else if(this.props.value === undefined && nextProps.value && ! nextProps.options) {
  			this.loadOptions(nextProps.value);
  		}
  	},
  	focus () {
  		this.refs.select.focus();
  	},
  	resetState () {
  		this._currentRequestId = -1;
  		this.setState({
  			isLoading: false,
  			options: [],
  		});
  	},
  	getResponseHandler (input) {
  		let _requestId = this._currentRequestId = requestId++;
  		return (err, data) => {
  			if (err) throw err;
  			if (!this.isMounted()) return;
  			updateCache(this.state.cache, input, data);
  			if (_requestId !== this._currentRequestId) return;
  			this.setState({
  				isLoading: false,
  				options: data && data.options || [],
  			});
  		};
  	},
  	loadOptions (input) {
  		if ( ! input.length) return;
  		if (this.props.ignoreAccents) input = stripDiacritics(input);
  		if (this.props.ignoreCase) input = input.toLowerCase();
  		this._lastInput = input;
  		if (input.length < this.props.minimumInput) {
  			return this.resetState();
  		}
  		let cacheResult = getFromCache(this.state.cache, input);
  		if (cacheResult) {
  			return this.setState({
  				options: cacheResult.options,
  			});
  		}
  		this.setState({
  			isLoading: true,
  		});
  		let responseHandler = this.getResponseHandler(input);
  		return thenPromise(this.props.loadOptions(input, responseHandler), responseHandler);
  	},
  	onChange (newValue) {
  		this.props.onChange(newValue);
  
  		let value;
  
  		if (newValue === null) {
  			value = [];
  		} else {
  			value = [newValue];
  		}
  		this.setState({
  			options: value
  		});
  	},
  	render () {
  		let { noResultsText } = this.props;
  		let { isLoading, options } = this.state;
  		if (this.props.isLoading) isLoading = true;
  		let placeholder = isLoading ? this.props.loadingPlaceholder : this.props.placeholder;
  		if (!options.length) {
  			if (this._lastInput.length < this.props.minimumInput) noResultsText = this.props.searchPromptText;
  			if (isLoading) noResultsText = this.props.searchingText;
  		}
  
  		return (
  			<Select
  				{...this.props}
  				ref="select"
  				onChange={this.onChange}
  				isLoading={isLoading}
  				noResultsText={noResultsText}
  				onInputChange={this.loadOptions}
  				options={options}
  				placeholder={placeholder}
  				/>
  		);
  	}
  });
  
  module.exports = Async;
  