Flattens a nested array (the nesting can be to any depth). If `isShallow`
is truey, the array will only be flattened a single level. If a callback
is provided each element of the array is passed through the callback before
flattening. The callback is bound to `thisArg` and invoked with three
arguments; (value, index, array).

If a property name is provided for `callback` the created "_.pluck" style
callback will return the property value of the given element.

If an object is provided for `callback` the created "_.where" style callback
will return `true` for elements that have the properties of the given object,
else `false`.

@static
@memberOf _
@category Arrays
@param {Array} array The array to flatten.
@param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
@param {Function|Object|string} [callback=identity] The function called
per iteration. If a property name or object is provided it will be used
to create a "_.pluck" or "_.where" style callback, respectively.
@param {*} [thisArg] The `this` binding of `callback`.
@returns {Array} Returns a new flattened array.
@example

_.flatten([1, [2], [3, [[4]]]]);
// => [1, 2, 3, 4];

_.flatten([1, [2], [3, [[4]]]], true);
// => [1, 2, 3, [[4]]];

var stooges = [
{ 'name': 'curly', 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] },
{ 'name': 'moe', 'quotes': ['Spread out!', 'You knucklehead!'] }
];

// using "_.pluck" callback shorthand
_.flatten(stooges, 'quotes');
// => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!']
function flatten(array, isShallow, callback, thisArg) {
      // juggle arguments
      if (typeof isShallow != 'boolean' && isShallow != null) {
        thisArg = callback;
        callback = !(thisArg && thisArg[isShallow] === array) ? isShallow : null;
        isShallow = false;
      }
      if (callback != null) {
        array = map(array, callback, thisArg);
      }
      return baseFlatten(array, isShallow);
    }