function tokenize( str, keepWhitespace ) {
	var subtkns;
	var substrs;
	var tokens;
	var substr;
	var cache;
	var i;
	if ( !isString( str ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', str ) );
	}
	if ( arguments.length > 1 ) {
		if ( !isBoolean( keepWhitespace ) ) {
			throw new TypeError( format( 'invalid argument. Second argument must be a boolean. Value: `%s`.', keepWhitespace ) );
		}
	}
	if ( !str ) {
		return [];
	}

	// Split on whitespace:
	if ( keepWhitespace ) {
		substrs = str.split( /(\s+)/ );
	} else {
		substrs = str.split( /\s+/ );
	}

	// Set up cache to hold tokens for substring matches:
	cache = {};

	// Initialize token array:
	tokens = [];

	for ( i = 0; i < substrs.length; i++ ) {
		substr = substrs[ i ];
		if ( hasOwnProp( cache, substr ) ) {
			extend( tokens, cache[ substr ] );
		}
		else {
			subtkns = tokenizeSubstring( substr );
			extend( tokens, subtkns );
			cache[ substr ] = subtkns;
		}
	}
	return tokens;
}
Tokenize a string.

@param {string} str - input string
@param {boolean} [keepWhitespace=false] - boolean indicating whether whitespace characters should be returned as part of the token array
@throws {TypeError} first argument must be a string
@throws {TypeError} second argument must be a boolean
@returns {Array} array of tokens

@example
var str = 'Hello World!';
var out = tokenize( str );
// returns [ 'Hello', 'World', '!' ]

@example
var str = '';
var out = tokenize( str );
// returns []

@example
var str = 'Hello Mrs. Maple, could you call me back?';
var out = tokenize( str );
// returns [ 'Hello', 'Mrs.', 'Maple', ',', 'could', 'you', 'call', 'me', 'back', '?' ]