/**
 * The JSON Reader is used by a Proxy to read a server response that is sent back in JSON format. This usually happens
 * as a result of loading a Store - for example we might create something like this:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'name', 'email']
 *         }
 *     });
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'ajax',
 *             url : 'users.json',
 *             reader: {
 *                 type: 'json'
 *             }
 *         }
 *     });
 *
 * The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're not
 * already familiar with them.
 *
 * We created the simplest type of JSON Reader possible by simply telling our {@link Ext.data.Store Store}'s {@link
 * Ext.data.proxy.Proxy Proxy} that we want a JSON Reader. The Store automatically passes the configured model to the
 * Store, so it is as if we passed this instead:
 *
 *     reader: {
 *         type : 'json',
 *         model: 'User'
 *     }
 *
 * The reader we set up is ready to read data from our server - at the moment it will accept a response like this:
 *
 *     [
 *         {
 *             "id": 1,
 *             "name": "Ed Spencer",
 *             "email": "ed@sencha.com"
 *         },
 *         {
 *             "id": 2,
 *             "name": "Abe Elias",
 *             "email": "abe@sencha.com"
 *         }
 *     ]
 *
 * ## Reading other JSON formats
 *
 * If you already have your JSON format defined and it doesn't look quite like what we have above, you can usually pass
 * JsonReader a couple of configuration options to make it parse your format. For example, we can use the
 * {@link #rootProperty} configuration to parse data that comes back like this:
 *
 *     {
 *         "users": [
 *            {
 *                "id": 1,
 *                "name": "Ed Spencer",
 *                "email": "ed@sencha.com"
 *            },
 *            {
 *                "id": 2,
 *                "name": "Abe Elias",
 *                "email": "abe@sencha.com"
 *            }
 *         ]
 *     }
 *
 * To parse this we just pass in a {@link #rootProperty} configuration that matches the 'users' above:
 *
 *     reader: {
 *         type: 'json',
 *         rootProperty: 'users'
 *     }
 *
 * Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata around
 * each record inside a nested structure like this:
 *
 *     {
 *         "total": 122,
 *         "offset": 0,
 *         "users": [
 *             {
 *                 "id": "ed-spencer-1",
 *                 "value": 1,
 *                 "user": {
 *                     "id": 1,
 *                     "name": "Ed Spencer",
 *                     "email": "ed@sencha.com"
 *                 }
 *             }
 *         ]
 *     }
 *
 * In the case above the record data is nested an additional level inside the "users" array as each "user" item has
 * additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the JSON
 * above we need to specify the {@link #record} configuration like this:
 *
 *     reader: {
 *         type: 'json',
 *         record: 'user',
 *         rootProperty: 'users'
 *     }
 *
 * ## Response MetaData
 *
 * The server can return metadata in its response, in addition to the record data, that describe attributes
 * of the data set itself or are used to reconfigure the Reader. To pass metadata in the response you simply
 * add a `metaData` attribute to the root of the response data. The metaData attribute can contain anything,
 * but supports a specific set of properties that are handled by the Reader if they are present:
 *
 * - {@link #idProperty}: property name for the primary key field of the data
 * - {@link #rootProperty}: the property name of the root response node containing the record data
 * - {@link #totalProperty}: property name for the total number of records in the data
 * - {@link #successProperty}: property name for the success status of the response
 * - {@link #messageProperty}: property name for an optional response message
 * - {@link Ext.data.Model#cfg-fields fields}: Config used to reconfigure the Model's fields before converting the
 *   response data into records
 *
 * An initial Reader configuration containing all of these properties might look like this ("fields" would be
 * included in the Model definition, not shown):
 *
 *     reader: {
 *         type: 'json',
 *         idProperty: 'id',
 *         rootProperty: 'root',
 *         totalProperty: 'total',
 *         successProperty: 'success',
 *         messageProperty: 'message'
 *     }
 *
 * If you were to pass a response object containing attributes different from those initially defined above, you could
 * use the `metaData` attribute to reconfigure the Reader on the fly. For example:
 *
 *     {
 *         "count": 1,
 *         "ok": true,
 *         "msg": "Users found",
 *         "users": [{
 *             "userId": 123,
 *             "name": "Ed Spencer",
 *             "email": "ed@sencha.com"
 *         }],
 *         "metaData": {
 *             "idProperty": 'userId',
 *             "rootProperty": "users",
 *             "totalProperty": 'count',
 *             "successProperty": 'ok',
 *             "messageProperty": 'msg'
 *         }
 *     }
 *
 * You can also place any other arbitrary data you need into the `metaData` attribute which will be ignored by the Reader,
 * but will be accessible via the Reader's {@link #metaData} property. Application code can then process the passed
 * metadata in any way it chooses.
 *
 * A simple example for how this can be used would be customizing the fields for a Model that is bound to a grid. By passing
 * the `fields` property the Model will be automatically updated by the Reader internally, but that change will not be
 * reflected automatically in the grid unless you also update the column configuration. You could do this manually, or you
 * could simply pass a standard grid column config object as part of the `metaData` attribute
 * and then pass that along to the grid. Here's a very simple example for how that could be accomplished:
 *
 *     // response format:
 *     {
 *         ...
 *         "metaData": {
 *             "fields": [
 *                 { "name": "userId", "type": "int" },
 *                 { "name": "name", "type": "string" },
 *                 { "name": "birthday", "type": "date", "dateFormat": "Y-j-m" },
 *             ],
 *             "columns": [
 *                 { "text": "User ID", "dataIndex": "userId", "width": 40 },
 *                 { "text": "User Name", "dataIndex": "name", "flex": 1 },
 *                 { "text": "Birthday", "dataIndex": "birthday", "flex": 1, "format": 'Y-j-m', "xtype": "datecolumn" }
 *             ]
 *         }
 *     }
 */
Ext.define('Ext.data.reader.Json', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',

    config: {
        /**
         * @cfg {String} [record=null]
         * The optional location within the JSON response that the record data itself can be found at. See the
         * JsonReader intro docs for more details. This is not often needed.
         */
        record: null,

        /**
         * @cfg {Boolean} [useSimpleAccessors=false]
         * `true` to ensure that field names/mappings are treated as literals when reading values. For
         * example, by default, using the mapping "foo.bar.baz" will try and read a property foo from the root, then a
         * property bar from foo, then a property baz from bar. Setting the simple accessors to `true` will read the
         * property with the name "foo.bar.baz" direct from the root object.
         */
        useSimpleAccessors: false
    },

    objectRe: /[\[\.]/,

    // @inheritdoc
    getResponseData: function(response) {
        var responseText = response;

        // Handle an XMLHttpRequest object
        if (response && response.responseText) {
            responseText = response.responseText;
        }

        // Handle the case where data has already been decoded
        if (typeof responseText !== 'string') {
            return responseText;
        }

        var data;
        try {
            data = Ext.decode(responseText);
        }
        catch (ex) {
            /**
             * @event exception Fires whenever the reader is unable to parse a response.
             * @param {Ext.data.reader.Xml} reader A reference to this reader.
             * @param {XMLHttpRequest} response The XMLHttpRequest response object.
             * @param {String} error The error message.
             */
            this.fireEvent('exception', this, response, 'Unable to parse the JSON returned by the server: ' + ex.toString());
            Ext.Logger.warn('Unable to parse the JSON returned by the server: ' + ex.toString());
        }
        //<debug>
        if (!data) {
            this.fireEvent('exception', this, response, 'JSON object not found');

            Ext.Logger.warn('JSON object not found');
        }
        //</debug>

        return data;
    },

    // @inheritdoc
    buildExtractors: function() {
        var me = this,
            root = me.getRootProperty();

        me.callParent(arguments);

        if (root) {
            me.rootAccessor = me.createAccessor(root);
        } else {
            delete me.rootAccessor;
        }
    },

    /**
     * We create this method because `root` is now a config so `getRoot` is already defined, but in the old
     * data package `getRoot` was passed a data argument and it would return the data inside of the `root`
     * property. This method handles both cases.
     * @param {Array/Object} [data]
     * @return {String/Object} Returns the config root value if this method was called without passing
     * data. Else it returns the object in the data bound to the root.
     * @private
     */
    getRoot: function(data) {
        var fieldsCollection = this.getModel().getFields();

        /*
         * We check here whether the fields are dirty since the last read.
         * This works around an issue when a Model is used for both a Tree and another
         * source, because the tree decorates the model with extra fields and it causes
         * issues because the readers aren't notified.
         */
        if (fieldsCollection.isDirty) {
            this.buildExtractors(true);
            delete fieldsCollection.isDirty;
        }

        if (this.rootAccessor) {
            return this.rootAccessor.call(this, data);
        } else {
            return data;
        }
    },

    /**
     * @private
     * We're just preparing the data for the superclass by pulling out the record objects we want. If a {@link #record}
     * was specified we have to pull those out of the larger JSON object, which is most of what this function is doing
     * @param {Object} root The JSON root node
     * @return {Ext.data.Model[]} The records
     */
    extractData: function(root) {
        var recordName = this.getRecord(),
            data = [],
            length, i;

        if (recordName) {
            length = root.length;

            if (!length && Ext.isObject(root)) {
                length = 1;
                root = [root];
            }

            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([data]);
    },

    /**
     * @private
     * Returns an accessor function for the given property string. Gives support for properties such as the following:
     * 'someProperty'
     * 'some.property'
     * 'some["property"]'
     * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances.
     */
    createAccessor: function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.getUseSimpleAccessors() !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'var value; try {value = obj' + (i > 0 ? '.' : '') + expr + '} catch(e) {}; return value;');
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }(),

    /**
     * @private
     * Returns an accessor expression for the passed Field. Gives support for properties such as the following:
     * 'someProperty'
     * 'some.property'
     * 'some["property"]'
     * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
     */
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var me     = this,
            re     = me.objectRe,
            hasMap = (field.getMapping() !== null),
            map    = hasMap ? field.getMapping() : field.getName(),
            result, operatorSearch;

        if (typeof map === 'function') {
            result = fieldVarName + '.getMapping()(' + dataName + ', this)';
        }
        else if (me.getUseSimpleAccessors() === true || ((operatorSearch = String(map).search(re)) < 0)) {
            if (!hasMap || isNaN(map)) {
                // If we don't provide a mapping, we may have a field name that is numeric
                map = '"' + map + '"';
            }
            result = dataName + "[" + map + "]";
        }
        else {
            result = dataName + (operatorSearch > 0 ? '.' : '') + map;
        }

        return result;
    }
});


import register from 'native-or-another/register';
import Promize from 'native-or-another';

var index = function (val) {
	if (val === null || val === undefined) {
		return [];
	}

	return Array.isArray(val) ? val : [val];
};

/**
 * An Array.prototype.slice.call(arguments) alternative
 *
 * @param {Object} args something with a length
 * @param {Number} slice
 * @param {Number} sliceEnd
 * @api public
 */

var index$1 = function (args, slice, sliceEnd) {
  var ret = [];
  var len = args.length;

  if (0 === len) { return ret; }

  var start = slice < 0
    ? Math.max(0, slice + len)
    : slice || 0;

  if (sliceEnd !== undefined) {
    len = sliceEnd < 0
      ? sliceEnd + len
      : sliceEnd;
  }

  while (len-- > start) {
    ret[len - start] = args[len];
  }

  return ret;
};

/*!
 * is-extendable <https://github.com/jonschlinkert/is-extendable>
 *
 * Copyright (c) 2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

var index$3 = function isExtendable(val) {
  return typeof val !== 'undefined' && val !== null
    && (typeof val === 'object' || typeof val === 'function');
};

var index$2 = function extend(o/*, objects*/) {
  var arguments$1 = arguments;

  if (!index$3(o)) { o = {}; }

  var len = arguments.length;
  for (var i = 1; i < len; i++) {
    var obj = arguments$1[i];

    if (index$3(obj)) {
      assign(o, obj);
    }
  }
  return o;
};

function assign(a, b) {
  for (var key in b) {
    if (hasOwn(b, key)) {
      a[key] = b[key];
    }
  }
}

/**
 * Returns true if the given `key` is an own property of `obj`.
 */

function hasOwn(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

/**
 * > Check any of `values` exists on `arr`.
 *
 * **Example**
 *
 * ```js
 * var arrIncludes = require('arr-includes')
 *
 * console.log(arrIncludes([1, 'bar', 55], 2)) // => false
 * console.log(arrIncludes([1, 'bar', 3], 3)) // => 2
 *
 * console.log(arrIncludes([1, 'bar', 3, true], false)) // => false
 * console.log(arrIncludes([1, 'bar', 44, true], true)) // => 3
 *
 * console.log(arrIncludes(['foo', 'bar'], 'baz')) // => false
 * console.log(arrIncludes(['foo', 'bar'], 'foo')) // => true
 * console.log(arrIncludes(['qux', 'foo', 'bar'], 'foo')) // => 1
 * console.log(arrIncludes([true, 'qqq', 'bar'], true)) // => true
 * console.log(arrIncludes(['true', 'qqq', 'bar'], true)) // => false
 * console.log(arrIncludes(['qqq', 'bar', true], true)) // => 2
 * console.log(arrIncludes(['qqq', 'true', 'bar'], true)) // => false
 * console.log(arrIncludes([false, 'foo', null, 'bar'], null)) // => 2
 *
 * console.log(arrIncludes(['foo', 'bar', 'qux'], ['a', 'b', 'c'])) // => false
 * console.log(arrIncludes(['b', 'a', 'c'], ['a', 'b', 'c'])) // => 1
 * console.log(arrIncludes(['foo', 'bb', 'b'], ['a', 'b'])) // => 2
 * console.log(arrIncludes(['foo', 'bar', 'qux'], ['a', 'b', 'foo'])) // => true
 * console.log(arrIncludes(['bar', 123, 'foo', 'qux'], ['a', 'b', 'foo'])) // => 2
 * ```
 *
 * @param  {Array} `arr` array to check
 * @param  {Array|String} `values` array or string
 * @return {Boolean|Number} returns `false` if not found, `true` if **index is 0**
 *                                  from the array, otherwise `number` index
 * @api public
 */

var index$6 = function arrIncludes (arr, values) {
  if (!Array.isArray(values)) { return inArray(arr, values) }
  var len = values.length;
  var i = -1;

  while (i++ < len) {
    var j = inArray(arr, values[i]);
    if (j) {
      return j
    }
  }

  return false
};

function inArray (arr, val) {
  arr = index(arr);
  var len = arr.length;
  var i = null;

  for (i = 0; i < len; i++) {
    if (arr[i] === val) {
      return i === 0 ? true : i
    }
  }
  return false
}

/*!
 * common-callback-names <https://github.com/tunnckoCore/common-callback-names>
 *
 * Copyright (c) Charlike Mike Reagent <@tunnckoCore> (https://i.am.charlike.online)
 * Released under the MIT license.
 */

var index$8 = [
  'callback',
  'callback_',
  'cb',
  'cb_',
  'done',
  'next'
];

/*!
 * function-arguments <https://github.com/tunnckoCore/function-arguments>
 *
 * Copyright (c) 2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

/**
 * > Get function arguments names.
 *
 * **Example**
 *
 * ```js
 * var fnArgs = require('function-arguments')
 *
 * console.log(fnArgs(function (a, b, c) {})) // => [ 'a', 'b', 'c' ]
 * console.log(fnArgs(function named (a , b, c) {})) // => [ 'a', 'b', 'c' ]
 *
 * console.log(fnArgs(a => {})) // => [ 'a' ]
 * console.log(fnArgs((a, b) => {})) // => [ 'a', 'b' ]
 *
 * console.log(fnArgs(function * (a ,b, c) {})) // => [ 'a', 'b', 'c' ]
 * console.log(fnArgs(function * named (a ,b, c) {})) // => [ 'a', 'b', 'c' ]
 * ```
 *
 * @param  {Function} `fn` Function from which to get arguments names.
 * @return {Array}
 * @api public
 */

var index$10 = function functionArguments (fn) {
  if (typeof fn !== 'function') {
    throw new TypeError('function-arguments expect a function')
  }
  if (fn.length === 0) {
    return []
  }

  // from https://github.com/jrburke/requirejs
  var reComments = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
  var fnToStr = Function.prototype.toString;
  var fnStr = fnToStr.call(fn);
  fnStr = fnStr.replace(reComments, '') || fnStr;
  fnStr = fnStr.slice(0, fnStr.indexOf('{'));

  var open = fnStr.indexOf('(');
  var close = fnStr.indexOf(')');

  open = open >= 0 ? open + 1 : 0;
  close = close > 0 ? close : fnStr.indexOf('=');

  fnStr = fnStr.slice(open, close);
  fnStr = '(' + fnStr + ')';

  var match = fnStr.match(/\(([\s\S]*)\)/);
  return match ? match[1].split(',').map(function (param) {
    return param.trim()
  }) : []
};

/**
 * > Trying to guess is `fn` asynchronous function or not.
 * But not [is-callback-function][] be aware of that diff.
 *
 * **Example**
 *
 * ```js
 * var fs = require('fs')
 * var isAsyncFn = require('is-async-function')
 *
 * console.log(isAsyncFunction(fs.readFile)) // => true
 * console.log(isAsyncFunction(fs.stat)) // => true
 *
 * console.log(isAsyncFunction(fs.readFileSync)) // => false
 * console.log(isAsyncFunction(fs.statSync)) // => false
 *
 * // or pass custom names to recognize as `async`
 * console.log(isAsyncFunction(fs.stat, ['cb'])) // => false
 * console.log(isAsyncFunction(fs.readFile, ['foo', 'bar']))
 * // => false, because fs.readFile uses `cb`
 * ```
 *
 * @param  {Function} `fn` is this `fn` a callback function
 * @param  {Array} `names` arguments names, default are [common-callback-names][]
 * @param  {Boolean} `strict` defaults to `true` to always return a boolean,
 *                            pass `false` to get index (position) - this is
 *                            useful when you wanna understand which "callback name"
 *                            exists as argument in that `fn`
 * @return {Boolean|Number} always boolean `true` or `false` when on strict mode,
 *                          othewise it can be Number index representing the position
 *                          and if index is 0 it is transformed to boolean `true` - so
 *                          always positive value if function is async.
 * @api public
 */

var index$5 = function isAsyncFunction (fn, names, strict) {
  if (typeof fn !== 'function') {
    throw new TypeError('is-async-function expect a function')
  }

  strict = typeof names === 'boolean' ? names : strict;
  strict = typeof strict === 'boolean' ? strict : true;
  names = typeof names === 'boolean' ? null : names;

  names = Array.isArray(names) ? names : index(names);
  names = names.length ? names : index$8;

  var idx = index$6(names, index$10(fn));
  return strict ? Boolean(idx) : idx
};

/*!
 * redolent <https://github.com/tunnckoCore/redolent>
 *
 * Copyright (c) Charlike Mike Reagent <@tunnckoCore> (https://i.am.charlike.online)
 * Released under the MIT license.
 */

/**
 * > Will try to promisify `fn` with native Promise,
 * otherwise you can give different promise module
 * to `opts.Promise`, for example [pinkie][] or [bluebird][].
 * If `fn` [is-async-function][] it will be passed with `done` callback
 * as last argument - always concatenated with the other provided args
 * through `opts.args`.
 *
 * **Note:** Uses [native-or-another][] for detection, so it will always will use the
 * native Promise, otherwise will try to load some of the common promise libraries
 * and as last resort if can't find one of them installed, then throws an Error!
 *
 * **Example**
 *
 * ```js
 * const fs = require('fs')
 * const request = require('request')
 * const redolent = require('redolent')
 *
 * redolent(fs.readFile)('package.json', 'utf-8').then(data => {
 *   console.log(JSON.parse(data).name)
 * })
 *
 * // handles multiple arguments by default
 * redolent(request)('http://www.tunnckocore.tk/').then(result => {
 *   const [httpResponse, body] = result
 * })
 *
 * // `a` and `b` arguments comes from `opts.args`
 * // `c` and `d` comes from the call of the promisified function
 * const fn = redolent((a, b, c, d, done) => {
 *   console.log(typeof done) // => 'function'
 *   done(null, a + b + c + d)
 * }, {
 *   args: [1, 2]
 * })
 *
 * fn(3, 5).then((res) => {
 *   console.log(res) // => 11
 * })
 * ```
 *
 * @name   redolent
 * @param  {Function} `<fn>` a function to be promisified
 * @param  {Object} `[opts]` optional options, also passed to [native-or-another][]
 * @param  {Array} `[opts.args]` additional arguments to be passed to `fn`,
 *                               all args from `opts.args` and these that are
 *                               passed to promisifed function are concatenated
 * @param  {Object} `[opts.context]` what context to be applied to `fn`,
 *                                   by default it is smart enough and applies
 *                                   the `this` context of redolent call or the call
 *                                   of the promisified function
 * @param  {Function} `[opts.Promise]` custom Promise constructor for versions `< v0.12`,
 *                                   like [bluebird][] for example, by default
 *                                   it **always** uses the native Promise in newer
 *                                   node versions
 * @param  {Boolean} `[opts.global]` defaults to `true`, pass false if you don't
 *                                   want to attach/add/register the given promise
 *                                   to the `global` scope, when node `< v0.12`
 * @return {Function} promisified function
 * @throws {TypeError} If `fn` is not a function
 * @throws {TypeError} If no promise is found
 * @api public
 */

function redolent (fn, opts) {
  if (typeof fn !== 'function') {
    throw new TypeError('redolent: expect `fn` to be a function')
  }

  opts = index$2({ context: this, Promise: Promize }, opts);
  opts.Promise = register(opts);

  // we can't test that here, because some
  // of our devDeps has some Promise library,
  // so it's loaded by `native-or-another` automatically
  /* istanbul ignore next */
  if (typeof opts.Promise !== 'function') {
    var msg = 'no native Promise support nor other promise were found';
    throw new TypeError('redolent: ' + msg)
  }

  return function () {
    opts.context = this || opts.context;
    opts.args = index(opts.args).concat(index$1(arguments));

    var promise = new opts.Promise(function (resolve, reject) {
      var called = false;

      function done (er, res) {
        called = true;
        if (er) {
          return reject(er)
        }
        if (arguments.length > 2) {
          res = index$1(arguments, 1);
        }
        return resolve(res)
      }

      var isAsyncFn = index$5(fn);

      opts.args = isAsyncFn ? opts.args.concat(done) : opts.args;
      var syncResult = fn.apply(opts.context, opts.args);

      var xPromise = isPromise(syncResult, opts.Promise);
      var hasPromiseReturn = isAsyncFn && !called && xPromise;

      if ((!isAsyncFn && !called) || hasPromiseReturn) {
        resolve(syncResult);
        return
      }
      if (isAsyncFn && !xPromise && syncResult !== undefined) {
        var msg = 'Asynchronous functions can only return a Promise or invoke a callback';
        reject(new Error('redolent: ' + msg));
      }
    });

    return normalize(promise, opts.Promise)
  }
}

function normalize (promise, Ctor) {
  promise.___nativePromise = Boolean(Ctor.___nativePromise);
  promise.___customPromise = Boolean(Ctor.___customPromise);
  return promise
}

/* istanbul ignore next */
function isPromise (val, Promize$$1) {
  return val instanceof Promize$$1 || (
    val !== null &&
    typeof val === 'object' &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

export default redolent;


/**
 * Url Utils
 *
 * ┌─────────────────────────────────────────────────────────────────────────────┐
 * │ href http://user:pass@subdomain.host.com:8080/p/a/t/h?query=string#hash.val │
 * ├──────────┬┬───────────┬─────────────────┬───────────────────────────┬───────┤
 * │ protocol ││   auth    │      host       │           path            │ hash  │
 * │          ││           ├──────────┬──────┼──────────┬────────────────┤       │
 * │          ││           │ hostname │ port │ pathname │     search     │       │
 * │          ││           │          │      │          ├─┬──────────────┤       │
 * │          ││           │          │      │          │ │    query     │       │
 * |  http:   // user:pass @ host.com : 8080   /p/a/t/h  ?  query=string   #hash |
 * │          ││           │          │      │          │ │              │       │
 * └──────────┴┴───────────┴──────────┴──────┴──────────┴─┴──────────────┴───────┘
 *
 * (all spaces in the "" line should be ignored -- they're purely for formatting)
 *
 * The layout of this js file from top->down is the same
 * as the graph above from left->right (larger first)
 *
 */
'use strict'

// http://user:pass@subdomain.host.com:8080/p/a/t/h?query=string#hash.val
// protocol: 'http:',
// slashes: true,
// auth: 'user:pass',
// host: 'subdomain.host.com:8080',
// port: '8080',
// hostname: 'subdomain.host.com',
// hash: '#hash.val',
// search: '?query=string',
// query: 'query=string',
// pathname: '/p/a/t/h',
// path: '/p/a/t/h?query=string',
// href: 'http://user:pass@subdomain.host.com:8080/p/a/t/h?query=string#hash.val'

'use strict'

const _ = require('lodash')
const url = require('url')
const _path = require('./path')
const _obj = require('./obj')

const REGX_VALID_URL = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
//const REGX_VALID_URL = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi
const  REGX_QUERY_OBJECT = /[?&]([^=#]+)=([^&#]*)/g

const ALL_EXCLUDE_KEYS = ['resolve', 'resolveObject', 'Url', 'all', 'isValid', 'parse']

exports = module.exports = url

_.assign(exports, {
  "isValid": isValid,
  "all": all,
  "clean": clean,
  "join": join,
  "href": href,
  "protocol": protocol,
  "slashes": slashes,
  "dir": dir,
  "hostpath": hostpath,
  "home": home,
  "auth": auth,
  "host": host,
  "hostname": hostname,
  "subdomain": subdomain,
  "port": port,
  "path": path,
  "pathname": pathname,
  "aname": aname,
  "name": name,
  "base" : base,
  "search": search,
  "query": query,
  "hash": hash,
  "jquery": jquery
})

_.assign(_, { url: exports })

function isValid(src) { return src ? REGX_VALID_URL.test(src) : false }

// returns result object with props containing each _.url[key] invoked result
function all(src) {
  let ret = {}
  _.forIn(exports, (v, k) => {
    if(!_.isFunction(v) || _.includes(ALL_EXCLUDE_KEYS, k)) return
    ret[k] = _.attempt(v, src)
  })
  return ret
}

// cleans url to be properly formatted
function clean(src) {
  let pidx = src.indexOf('http')
  if(pidx > 0) src = src.substr(pidx)
  return src ? pidx >= 0 || src.indexOf('//') >= 0 ? src : '/' + src : ''
}

function join() {
  let args = _.args(_.toArray(arguments))
  let rando = _.uniqueId()
  let joins = []

  _.each(args, (arg) => {
    let targ = _.attempt(_.toString, arg)
    if(_.isError(targ)) return
    targ = targ.replace(/\/\//g, rando)
    joins = _.concat(joins, _.compact(targ.split(/\//g)))
  })
  joins = _.compact(joins)
  return clean(_.join(joins, '/').replace(rando, '//'))
}

function href(src) { src = clean(src); return src ? _.get(url.parse(src), 'href') : ''  }
function protocol(src) { src = clean(src); return src ? _.get(url.parse(src), 'protocol') : ''  }
function slashes(src) { src = clean(src); return src ? _.get(url.parse(src), 'slashes') : ''  }

function dir() {
  let src = join(arguments)
   return src ? _.path.dir(src) : ''
}

function hostpath(src) { src = clean(src); return src ? _.join([protocol(src), "//", host(src), pathname(src)], "") : ''  }
function home(src) { src = clean(src); return src ? _.join([protocol(src), "//", host(src)], "") : ''  }
function auth(src) { src = clean(src); return src ? _.get(url.parse(src), 'auth') : ''  }
function host(src) { src = clean(src); return src ? _.get(url.parse(src), 'host') : ''  }
function hostname(src) { src = clean(src); return src ? _.get(url.parse(src), 'hostname') : ''  }
function subdomain(src) { src = clean(src); return /[^.]+/.exec(_.replace(_.replace(src, 'http://'), 'https://'))[0] }
function port(src) { src = clean(src); return src ? _.get(url.parse(src), 'port') : ''  }
function path(src) { src = clean(src); return src ? _.get(url.parse(src), 'path') : ''  }
function pathname(src) { src = clean(src); return src ? _.get(url.parse(src), 'pathname') : ''  }

function aname() {
  let src = join(arguments)
  let srcdir = src ? dir(src) : ''
  let srcname = src ? name(src) : ''
  return join(srcdir, srcname)
}

function name(src) { src = clean(src); return src ? _.path.name(src) : '' }
function base(src) { src = clean(src); return src ? _.path.base(src) : '' }
function search(src) { src = clean(src); return src ? _.get(url.parse(src), 'search') : ''  }
function query(src) { src = clean(src); return src ? _.get(url.parse(src), 'query') : ''  }
function hash(src) { src = clean(src); return src ? _.get(url.parse(src), 'hash') : ''  }

// return json query object
function jquery(src) {
  src = clean(src);
  let params = {}
  let match = null
  if (!url || !_.isString(src)) return params
  while (match = REGX_QUERY_OBJECT.exec(src)) { params[match[1]] = match[2] }
  return params
}


'use strict';

// MODULES //

var isObject = require( 'validate.io-object' ),
	isBoolean = require( 'validate.io-boolean-primitive' );

// VALIDATE //

/**
* FUNCTION: validate( opts, options )
*	Validates function options.
*
* @param {Object} opts - destination for validated options
* @param {Object} options - function options
* @param {Boolean} [options.copy] - boolean indicating whether to return a new data structure
* @returns {Null|Error} null or an error
*/
function validate( opts, options ) {
	if ( !isObject( options ) ) {
		return new TypeError( 'transpose()::invalid input argument. Options argument must be an object. Value: `' + options + '`.' );
	}
	if ( options.hasOwnProperty( 'copy' ) ) {
		opts.copy = options.copy;
		if ( !isBoolean( opts.copy ) ) {
			return new TypeError( 'transpose()::invalid option. Copy option must be a boolean primitive. Option: `' + opts.copy + '`.' );
		}
	}
	return null;
} // end FUNCTION validate()

// EXPORTS //

module.exports = validate;


/*
 * typesettin-style-guide
 * http://github.com/typesettin/typesettin-style-guide
 *
 * Copyright (c) 2014 Yaw Joseph Etse. All rights reserved.
 */
'use strict';

var extend = require('util-extend'),
	classie = require('classie'),
	events = require('events'),
	util = require('util');

/**
 * A module that represents a componentTabs object, a componentTab is a page composition tool.
 * @{@link https://github.com/typesettin/typesettin-style-guide}
 * @author Yaw Joseph Etse
 * @copyright Copyright (c) 2014 Typesettin. All rights reserved.
 * @license MIT
 * @constructor componentTabs
 * @requires module:util-extent
 * @requires module:util
 * @requires module:events
 * @param {object} el element of tab container
 * @param {object} options configuration options
 */
var componentTabs = function (el, options) {
	events.EventEmitter.call(this);

	this.el = el;
	this.options = extend({}, this.options);
	extend(this.options, options);
	this.showTab = this._show;
	this._init();
};

util.inherits(componentTabs, events.EventEmitter);

/** module default configuration */
componentTabs.prototype.options = {
	start: 0,
	tabselector: 'nav > ul > li',
	itemselector: '.content > section',
	currenttabclass: 'tab-current',
	currentitemclass: 'content-current'
};
/**
 * initializes tabs and shows current tab.
 * @emits tabsInitialized
 */
componentTabs.prototype._init = function () {
	// tabs elemes
	this.tabs = [].slice.call(this.el.querySelectorAll(this.options.tabselector));
	// content items
	this.items = [].slice.call(this.el.querySelectorAll(this.options.itemselector));
	// current index
	this.current = -1;
	// show current content item
	this._show();
	// init events
	this._initEvents();
	if (this.options.callback) {
		this.options.callback();
	}
	this.emit('tabsInitialized');

};
/**
 * handle tab click events.
 */
componentTabs.prototype._initEvents = function () {
	var self = this;

	this.tabs.forEach(function (tab, idx) {
		tab.addEventListener('click', function (ev) {
			ev.preventDefault();
			self._show(idx);
		});
	});
	this.emit('tabsEventsInitialized');
};
/**
 * Sets up a new lintotype component.
 * @param {number} idx tab to show
 * @emits tabsShowIndex
 */
componentTabs.prototype._show = function (idx) {
	if (this.current >= 0) {
		classie.remove(this.tabs[this.current], this.options.currenttabclass);
		classie.remove(this.items[this.current], this.options.currentitemclass);
	}
	// change current
	this.current = idx !== undefined ? idx : this.options.start >= 0 && this.options.start < this.items.length ? this.options.start : 0;
	classie.add(this.tabs[this.current], this.options.currenttabclass);
	classie.add(this.items[this.current], this.options.currentitemclass);
	this.emit('tabsShowIndex', this.current);
};
module.exports = componentTabs;


const isUtils = require('./is.util');
const stringUtils = require('./string.util');

const {isFunction, isStringNumberBoolOrDate, isObject, isDate, isNumber, isString, isArray, isEmpty} = isUtils;
const {camelize} = stringUtils;

function hasNonEmptyValues(obj) {
    return Object.values(obj || {}).some(entry => !isEmpty(entry));
}

function stringifyArrayValues(obj = {}) {
    const parsed = {};

    Object.entries(obj).forEach((entry) => {
        const val = entry[1];

        if (isStringNumberBoolOrDate(val)) {
            /* Leave any non-array, primitive values alone */
            parsed[entry[0]] = val;
        } else if (isArray(val) && !isEmpty(val)) {
            parsed[entry[0]] = val.join(',');
        }
    });

    return parsed;
}

function arrayMe(val) {
    if (isArray(val)) return val;

    if (isObject(val) || isNumber(val) || isString(val) || isDate(val)) {
        return [val];
    }

    /* Sets, and other things Array.from() can convert */
    return Array.from(val);
}

function dateMe(dateValue) {
    if (isDate(dateValue)) return dateValue;
    if (isString(dateValue) || isNumber(dateValue)) {
        return new Date(dateValue);
    }

    return null;
}

function shuffle(a) {
    /* eslint no-plusplus: "off" */
    const cloned = [...a];

    for (let i = cloned.length; i; i--) {
        const j = Math.floor(Math.random() * i);
        [cloned[i - 1], cloned[j]] = [cloned[j], cloned[i - 1]];
    }

    return cloned;
}

function getNestedProperty(obj, dotPath) {
    let subGroup = Object.assign({}, obj),
        failedToFind;

    (isString(dotPath) ? dotPath.split('.') : []).forEach((prop) => {
        if (isObject(subGroup) && Object.keys(subGroup).includes(prop)) {
            subGroup = subGroup[prop];
        } else {
            failedToFind = true;
        }
    });

    return failedToFind ? '' : subGroup;
}

/**
* Transforms the properties in an object to an encoded URI query string.
* <b>Note</b>: works best if the object is in the format of
*   { propertyName: propertyValue, propertyName2: propertyValue2 . . }
* @method utilitarian.obj.util#toURI
* @param {object} obj A JSON object to serialize into a query string
* @param {boolean} [dontEncode] An optional boolean value to control whether
* or not to URI encode the resulting query string (defaults to `false`)
* @returns {string} A query string representation of the original object
*/
function toURI(obj, dontEncode) {
    const arr = [];
    let paramVal;

    if (isObject(obj) && !isArray(obj)) {
        Object.keys(obj).forEach((val) => {
            if (isArray(obj[val])) {
                paramVal = `[${obj[val].join(',')}]`;
            } else {
                paramVal = obj[val];
            }

            if (dontEncode) {
                arr.push(`${val}=${paramVal}`);
            } else {
                arr.push(
                    `${encodeURIComponent(val)}=${encodeURIComponent(paramVal)}`
                );
            }
        });
    }

    return arr.join('&');
}

/**
* Serializes an object's properties into JSON string then URI encoded
* @method utilitarian.obj.util#toParam
* @param {object} obj A JSON object to serialize into a query string parameter
* @param {boolean} [dontEncode] An optional boolean value to control whether or not to URI encode the resulting query parameter (defaults to `false`)
* @returns {string} A query string parameter representation of the original object
*/
function toParam(obj, dontEncode) {
    const arr = [];
    let vals;

    if (isObject(obj) && !isArray(obj)) {
        Object.keys(obj).forEach((val) => {
            if (isArray(obj[val])) {
                vals = `[${
                    obj[val].map(v => (isNaN(v) ? `"${v}"` : v)).join(',')
                }]`;
            } else {
                vals = isNaN(obj[val]) ? `"${obj[val]}"` : obj[val];
            }
            arr.push(`${val}:${vals}`);
        });

        if (dontEncode) {
            return `{${arr.join(',')}}`;
        }

        return encodeURIComponent(`{${arr.join(',')}}`);
    }

    return '';
}

function flatten(arr = []) {
    return [].concat(...arr);
}

function uniqueMe(arr = []) {
    return Array.from(new Set(arr));
}

function uniqueBy(arr = [], field = 'id') {
    return uniqueMe(arr.map(s => s[field]))
        .map(u => arr.find(a => a[field] === u))
        .filter(f => !isEmpty(f));
}

function formatObject(obj) {
    const oj = {};
    Object.keys(obj).forEach(key => Object.assign(oj, {[camelize(key)]: obj[key]}));

    return oj;
}

function mapEntries(obj, mapFn) {
    const fn = isFunction(mapFn) ? mapFn : entry => (entry);

    return Object.entries(obj).map(fn)
        .reduce((a, b) => Object.assign(a, {
            [b[0]]: b[1]
        }), {});
}

function pick(obj = {}, values = []) {
    const mappedObj = {};

    Object.keys(obj).filter(key => values.includes(key)).forEach((key) => {
        mappedObj[key] = obj[key];
    });

    return mappedObj;
}

function without(obj = {}, values = []) {
    const mappedObj = {};

    Object.keys(obj).filter(key => !values.includes(key)).forEach((key) => {
        mappedObj[key] = obj[key];
    });

    return mappedObj;
}

function intersect(ids) {
    if (!isArray(ids) || ids.some(i => isEmpty(arrayMe(i)))) {
        return [];
    }

    let intersectedIds = uniqueMe(flatten(ids.map(arrayMe)));

    ids.forEach((i) => {
        intersectedIds = arrayMe(i).filter(id => intersectedIds.includes(id));
    });

    return intersectedIds;
}

function pruneEmpties(obj) {
    const mappedObj = {};
    Object.entries(obj || {}).filter(entry => !isEmpty(entry[1])).forEach((entry) => {
        mappedObj[entry[0]] = entry[1];
    });

    return mappedObj;
}

function stack(array, iterator) {
    const results = [];
    const callback = isFunction(iterator) ? iterator :
        item => (isFunction(item) ? item() : item);

    let chain = Promise.resolve();
    array.forEach((item, index, arr) => {
        results.push(
            chain = chain.then(() => callback(item, index, arr))
        );
    });

    return Promise.all(results);
}

function spread(array, iterator) {
    const callback = isFunction(iterator) ? iterator :
        item => (isFunction(item) ? item() : item);

    return Promise.all(array.map(callback));
}

function move(array, currentIndex = 0, newIndex = 0) {
    const cloned = [...array];

    let adjustedIndex = 0;
    if (newIndex > 0) {
        if (newIndex >= cloned.length) {
            adjustedIndex = cloned.length;
        } else {
            adjustedIndex = newIndex;
        }
    }
    cloned.splice(adjustedIndex, 0, ...cloned.splice(currentIndex, 1));

    return cloned;
}

const ObjectUtils = {
    without,
    pick,
    hasNonEmptyValues,
    stringifyArrayValues,
    toURI,
    toParam,
    formatObject,
    pruneEmpties,
    mapEntries,
    getNestedProperty,
    nested: getNestedProperty
};

const ArrayUtils = {
    shuffle,
    flatten,
    intersect,
    move,
    uniqueMe,
    uniqueBy,
    stack,
    spread
};

function setObjectPrototypes() {
    Object.keys(ObjectUtils)
        .filter(prop => !Object.keys(Object.prototype).includes(prop))
        .forEach((prop) => {
            /* eslint func-names: "off" */
            Object.assign(Object.prototype, {
                [prop]: function(...args) {
                    return args.length ?
                        ObjectUtils[prop].apply(this, [this, ...args]) :
                        ObjectUtils[prop](this);
                }
            });
        });
}

function setArrayPrototypes() {
    Object.keys(ArrayUtils)
        .filter(prop => !Object.keys(Array.prototype).includes(prop))
        .forEach((prop) => {
            /* eslint func-names: "off" */
            Object.assign(Array.prototype, {
                [prop]: function(...args) {
                    return args.length ?
                        ArrayUtils[prop].apply(this, [this, ...args]) :
                        ArrayUtils[prop](this);
                }
            });
        });
}

Object.assign(exports, ObjectUtils, ArrayUtils, {
    arrayMe,
    dateMe,
    setArrayPrototypes,
    setObjectPrototypes
});


/**
 * @private
 */
Ext.define('Ext.device.geolocation.Abstract', {
    config: {
        /**
         * @cfg {Number} maximumAge
         * This option indicates that the application is willing to accept cached location information whose age
         * is no greater than the specified time in milliseconds. If maximumAge is set to 0, an attempt to retrieve
         * new location information is made immediately.
         */
        maximumAge: 0,

        /**
         * @cfg {Number} frequency The default frequency to get the current position when using {@link Ext.device.Geolocation#watchPosition}.
         */
        frequency: 10000,

        /**
         * @cfg {Boolean} allowHighAccuracy True to allow high accuracy when getting the current position.
         */
        allowHighAccuracy: false,

        /**
         * @cfg {Number} timeout
         * The maximum number of milliseconds allowed to elapse between a location update operation.
         */
        timeout: Infinity
    },

    /**
     * Attempts to get the current position of this device.
     *
     *     Ext.device.Geolocation.getCurrentPosition({
     *         success: function(position) {
     *             console.log(position);
     *         },
     *         failure: function() {
     *             Ext.Msg.alert('Geolocation', 'Something went wrong!');
     *         }
     *     });
     *
     * *Note:* If you want to watch the current position, you could use {@link Ext.device.Geolocation#watchPosition} instead.
     *
     * @param {Object} config An object which contains the following config options:
     *
     * @param {Function} config.success
     * The function to call when the location of the current device has been received.
     *
     * @param {Object} config.success.position
     *
     * @param {Function} config.failure
     * The function that is called when something goes wrong.
     *
     * @param {Object} config.scope
     * The scope of the `success` and `failure` functions.
     *
     * @param {Number} config.maximumAge
     * The maximum age of a cached location. If you do not enter a value for this, the value of {@link #maximumAge}
     * will be used.
     *
     * @param {Number} config.timeout
     * The timeout for this request. If you do not specify a value, it will default to {@link #timeout}.
     *
     * @param {Boolean} config.allowHighAccuracy
     * True to enable allow accuracy detection of the location of the current device. If you do not specify a value, it will
     * default to {@link #allowHighAccuracy}.
     */
    getCurrentPosition: function(config) {
        var defaultConfig = Ext.device.geolocation.Abstract.prototype.config;

        config = Ext.applyIf(config, {
            maximumAge: defaultConfig.maximumAge,
            frequency: defaultConfig.frequency,
            allowHighAccuracy: defaultConfig.allowHighAccuracy,
            timeout: defaultConfig.timeout
        });

        // <debug>
        if (!config.success) {
            Ext.Logger.warn('You need to specify a `success` function for #getCurrentPosition');
        }
        // </debug>

        return config;
    },

    /**
     * Watches for the current position and calls the callback when successful depending on the specified {@link #frequency}.
     *
     *     Ext.device.Geolocation.watchPosition({
     *         callback: function(position) {
     *             console.log(position);
     *         },
     *         failure: function() {
     *             Ext.Msg.alert('Geolocation', 'Something went wrong!');
     *         }
     *     });
     *
     * @param {Object} config An object which contains the following config options:
     *
     * @param {Function} config.callback
     * The function to be called when the position has been updated.
     *
     * @param {Function} config.failure
     * The function that is called when something goes wrong.
     *
     * @param {Object} config.scope
     * The scope of the `success` and `failure` functions.
     *
     * @param {Boolean} config.frequency
     * The frequency in which to call the supplied callback. Defaults to {@link #frequency} if you do not specify a value.
     *
     * @param {Boolean} config.allowHighAccuracy
     * True to enable allow accuracy detection of the location of the current device. If you do not specify a value, it will
     * default to {@link #allowHighAccuracy}.
     */
    watchPosition: function(config) {
        var defaultConfig = Ext.device.geolocation.Abstract.prototype.config;

        config = Ext.applyIf(config, {
            maximumAge: defaultConfig.maximumAge,
            frequency: defaultConfig.frequency,
            allowHighAccuracy: defaultConfig.allowHighAccuracy,
            timeout: defaultConfig.timeout
        });

        // <debug>
        if (!config.callback) {
            Ext.Logger.warn('You need to specify a `callback` function for #watchPosition');
        }
        // </debug>

        return config;
    },

    /**
     * If you are currently watching for the current position, this will stop that task.
     */
    clearWatch: function() {}
});


//@tag dom,core
//@define Ext.Element-all
//@define Ext.Element-alignment
//@require Ext.Element-static

/**
 * @class Ext.dom.Element
 */
//<deprecated product=touch since=2.0>
Ext.dom.Element.addMembers({
    /**
     * Gets the x,y coordinates specified by the anchor position on the element.
     *
     * @deprecated 2.0.0 This method is no longer available for Ext.Element. Please see {@link Ext.Component#showBy}
     * to do anchoring at Component level instead.
     *
     * @param {String} [anchor=c] (optional) The specified anchor position.
     * @param {Boolean} local (optional) `true` to get the local (element top/left-relative) anchor position instead
     * of page coordinates.
     * @param {Object} size (optional) An object containing the size to use for calculating anchor position.
     * `{width: (target width), height: (target height)}` (defaults to the element's current size)
     * @return {Array} [x, y] An array containing the element's x and y coordinates.
     */
    getAnchorXY: function(anchor, local, size) {
        //<debug warn>
        Ext.Logger.deprecate("getAnchorXY() is no longer available for Ext.Element. Please see Ext.Component#showBy() " +
            "to do anchoring at Component level instead", this);
        //</debug>

        //Passing a different size is useful for pre-calculating anchors,
        //especially for anchored animations that change the el size.
        anchor = (anchor || "tl").toLowerCase();
        size = size || {};

        var me = this,
            vp = me.dom == document.body || me.dom == document,
            width = size.width || vp ? window.innerWidth: me.getWidth(),
            height = size.height || vp ? window.innerHeight: me.getHeight(),
            xy,
            rnd = Math.round,
            myXY = me.getXY(),
            extraX = vp ? 0: !local ? myXY[0] : 0,
            extraY = vp ? 0: !local ? myXY[1] : 0,
            hash = {
                c: [rnd(width * 0.5), rnd(height * 0.5)],
                t: [rnd(width * 0.5), 0],
                l: [0, rnd(height * 0.5)],
                r: [width, rnd(height * 0.5)],
                b: [rnd(width * 0.5), height],
                tl: [0, 0],
                bl: [0, height],
                br: [width, height],
                tr: [width, 0]
            };

        xy = hash[anchor];
        return [xy[0] + extraX, xy[1] + extraY];
    },

    alignToRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    /**
     * Gets the x,y coordinates to align this element with another element.
     * @param {Mixed} element The element to align to.
     * @param {String} [position=tl-bl] (optional) The position to align to.
     * @param {Array} [offsets] Offset the positioning by [x, y].
     * @return {Array} [x, y]
     */
    getAlignToXY: function(el, position, offsets, local) {
        //<debug warn>
        Ext.Logger.deprecate("getAlignToXY() is no longer available for Ext.Element. Please see Ext.Component#showBy() " +
            "to do anchoring at Component level instead", this);
        //</debug>

        local = !!local;
        el = Ext.get(el);

        //<debug>
        if (!el || !el.dom) {
            throw new Error("Element.alignToXY with an element that doesn't exist");
        }
        //</debug>
        offsets = offsets || [0, 0];

        if (!position || position == '?') {
            position = 'tl-bl?';
        }
        else if (! (/-/).test(position) && position !== "") {
            position = 'tl-' + position;
        }
        position = position.toLowerCase();

        var me = this,
            matches = position.match(this.alignToRe),
            dw = window.innerWidth,
            dh = window.innerHeight,
            p1 = "",
            p2 = "",
            a1,
            a2,
            x,
            y,
            swapX,
            swapY,
            p1x,
            p1y,
            p2x,
            p2y,
            width,
            height,
            region,
            constrain;

        if (!matches) {
            throw "Element.alignTo with an invalid alignment " + position;
        }

        p1 = matches[1];
        p2 = matches[2];
        constrain = !!matches[3];

        //Subtract the aligned el's internal xy from the target's offset xy
        //plus custom offset to get the aligned el's new offset xy
        a1 = me.getAnchorXY(p1, true);
        a2 = el.getAnchorXY(p2, local);

        x = a2[0] - a1[0] + offsets[0];
        y = a2[1] - a1[1] + offsets[1];

        if (constrain) {
            width = me.getWidth();
            height = me.getHeight();

            region = el.getPageBox();

            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
            //perpendicular to the vp border, allow the aligned el to slide on that border,
            //otherwise swap the aligned el to the opposite border of the target.
            p1y = p1.charAt(0);
            p1x = p1.charAt(p1.length - 1);
            p2y = p2.charAt(0);
            p2x = p2.charAt(p2.length - 1);

            swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + width > dw) {
                x = swapX ? region.left - width: dw - width;
            }
            if (x < 0) {
                x = swapX ? region.right: 0;
            }
            if (y + height > dh) {
                y = swapY ? region.top - height: dh - height;
            }
            if (y < 0) {
                y = swapY ? region.bottom: 0;
            }
        }

        return [x, y];
    },

    // @private
    getAnchor: function(){
        var dom = this.dom;
            if (!dom) {
                return;
            }
            var anchor = this.self.data.call(this.self, dom, '_anchor');

        if(!anchor){
            anchor = this.self.data.call(this.self, dom, '_anchor', {});
        }
        return anchor;
    },

    // @private
    // used outside of core
    adjustForConstraints: function(xy, parent) {
        var vector = this.getConstrainVector(parent, xy);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    }

});
//</deprecated>


/**
 * @private
 */
Ext.define('Ext.util.AbstractMixedCollection', {
    requires: ['Ext.util.Filter'],

    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    /**
     * @event clear
     * Fires when the collection is cleared.
     */

    /**
     * @event add
     * Fires when an item is added to the collection.
     * @param {Number} index The index at which the item was added.
     * @param {Object} o The item added.
     * @param {String} key The key associated with the added item.
     */

    /**
     * @event replace
     * Fires when an item is replaced in the collection.
     * @param {String} key he key associated with the new added.
     * @param {Object} old The item being replaced.
     * @param {Object} new The new item.
     */

    /**
     * @event remove
     * Fires when an item is removed from the collection.
     * @param {Object} o The item being removed.
     * @param {String} key (optional) The key associated with the removed item.
     */

    /**
     * Creates new MixedCollection.
     * @param {Boolean} [allowFunctions=false] Specify `true` if the {@link #addAll}
     * function should add function references to the collection.
     * @param {Function} [keyFn] A function that can accept an item of the type(s) stored in this MixedCollection
     * and return the key value for that item.  This is used when available to look up the key on items that
     * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
     * equivalent to providing an implementation for the {@link #getKey} method.
     */
    constructor: function(allowFunctions, keyFn) {
        var me = this;

        me.items = [];
        me.map = {};
        me.keys = [];
        me.length = 0;

        me.allowFunctions = allowFunctions === true;

        if (keyFn) {
            me.getKey = keyFn;
        }

        me.mixins.observable.constructor.call(me);
    },

    /**
     * @cfg {Boolean} allowFunctions Specify `true` if the {@link #addAll}
     * function should add function references to the collection.
     */
    allowFunctions : false,

    /**
     * Adds an item to the collection. Fires the {@link #event-add} event when complete.
     * @param {String} key The key to associate with the item, or the new item.
     *
     * If a {@link #getKey} implementation was specified for this MixedCollection,
     * or if the key of the stored items is in a property called `id`,
     * the MixedCollection will be able to _derive_ the key for the new item.
     * In this case just pass the new item in this parameter.
     * @param {Object} obj The item to add.
     * @return {Object} The item added.
     */
    add: function(key, obj){
        var me = this,
            myObj = obj,
            myKey = key,
            old;

        if (arguments.length == 1) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (typeof myKey != 'undefined' && myKey !== null) {
            old = me.map[myKey];
            if (typeof old != 'undefined') {
                return me.replace(myKey, myObj);
            }
            me.map[myKey] = myObj;
        }
        me.length++;
        me.items.push(myObj);
        me.keys.push(myKey);
        me.fireEvent('add', me.length - 1, myObj, myKey);
        return myObj;
    },

    /**
     * MixedCollection has a generic way to fetch keys if you implement `getKey`.  The default implementation
     * simply returns `item.id` but you can provide your own implementation
     * to return a different value as in the following examples:
     *
     *     // normal way
     *     var mc = new Ext.util.MixedCollection();
     *     mc.add(someEl.dom.id, someEl);
     *     mc.add(otherEl.dom.id, otherEl);
     *     //and so on
     *
     *     // using getKey
     *     var mc = new Ext.util.MixedCollection();
     *     mc.getKey = function(el) {
     *         return el.dom.id;
     *     };
     *     mc.add(someEl);
     *     mc.add(otherEl);
     *
     *     // or via the constructor
     *     var mc = new Ext.util.MixedCollection(false, function(el) {
     *         return el.dom.id;
     *     });
     *     mc.add(someEl);
     *     mc.add(otherEl);
     *
     * @param {Object} item The item for which to find the key.
     * @return {Object} The key for the passed item.
     */
    getKey: function(o){
         return o.id;
    },

    /**
     * Replaces an item in the collection. Fires the {@link #event-replace} event when complete.
     * @param {String} key The key associated with the item to replace, or the replacement item.
     *
     * If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
     * of your stored items is in a property called `id`, then the MixedCollection
     * will be able to _derive_ the key of the replacement item. If you want to replace an item
     * with one having the same key value, then just pass the replacement item in this parameter.
     * @param {Object} o (optional) If the first parameter passed was a key, the item to associate
     * with that key.
     * @return {Object}  The new item.
     */
    replace: function(key, o){
        var me = this,
            old,
            index;

        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
             return me.add(key, o);
        }
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        me.fireEvent('replace', key, old, o);
        return o;
    },

    /**
     * Adds all elements of an Array or an Object to the collection.
     * @param {Object/Array} objs An Object containing properties which will be added
     * to the collection, or an Array of values, each of which are added to the collection.
     * Functions references will be added to the collection if `{@link #allowFunctions}`
     * has been set to `true`.
     */
    addAll: function(objs){
        var me = this,
            i = 0,
            args,
            len,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            args = arguments.length > 1 ? arguments : objs;
            for (len = args.length; i < len; i++) {
                me.add(args[i]);
            }
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    /**
     * Executes the specified function once for every item in the collection.
     *
     * @param {Function} fn The function to execute for each item.
     * @param {Mixed} fn.item The collection item.
     * @param {Number} fn.index The item's index.
     * @param {Number} fn.length The total number of items in the collection.
     * @param {Boolean} fn.return Returning `false` will stop the iteration.
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
     * Defaults to the current item in the iteration.
     */
    each: function(fn, scope){
        var items = [].concat(this.items), // each safe for removal
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    /**
     * Executes the specified function once for every key in the collection, passing each
     * key, and its associated item as the first two parameters.
     * @param {Function} fn The function to execute for each item.
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the browser window.
     */
    eachKey: function(fn, scope){
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;

        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },

    /**
     * Returns the first item in the collection which elicits a `true` return value from the
     * passed selection function.
     * @param {Function} fn The selection function to execute for each item.
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the browser window.
     * @return {Object} The first item in the collection which returned `true` from the selection function.
     */
    findBy: function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },

    /**
     * Inserts an item at the specified index in the collection. Fires the `{@link #event-add}` event when complete.
     * @param {Number} index The index to insert the item at.
     * @param {String} key The key to associate with the new item, or the item itself.
     * @param {Object} [obj] If the second parameter was a key, the new item.
     * @return {Object} The item inserted.
     */
    insert: function(index, key, obj){
        var me = this,
            myKey = key,
            myObj = obj;

        if (arguments.length == 2) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (me.containsKey(myKey)) {
            me.suspendEvents();
            me.removeAtKey(myKey);
            me.resumeEvents();
        }
        if (index >= me.length) {
            return me.add(myKey, myObj);
        }
        me.length++;
        Ext.Array.splice(me.items, index, 0, myObj);
        if (typeof myKey != 'undefined' && myKey !== null) {
            me.map[myKey] = myObj;
        }
        Ext.Array.splice(me.keys, index, 0, myKey);
        me.fireEvent('add', index, myObj, myKey);
        return myObj;
    },

    /**
     * Remove an item from the collection.
     * @param {Object} o The item to remove.
     * @return {Object} The item removed or `false` if no item was removed.
     */
    remove: function(o){
        return this.removeAt(this.indexOf(o));
    },

    /**
     * Remove all items in the passed array from the collection.
     * @param {Array} items An array of items to be removed.
     * @return {Ext.util.MixedCollection} this object
     */
    removeAll: function(items){
        Ext.each(items || [], function(item) {
            this.remove(item);
        }, this);

        return this;
    },

    /**
     * Remove an item from a specified index in the collection. Fires the `{@link #event-remove}` event when complete.
     * @param {Number} index The index within the collection of the item to remove.
     * @return {Object/Boolean} The item removed or `false` if no item was removed.
     */
    removeAt: function(index){
        var me = this,
            o,
            key;

        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            Ext.Array.erase(me.items, index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            Ext.Array.erase(me.keys, index, 1);
            me.fireEvent('remove', o, key);
            return o;
        }
        return false;
    },

    /**
     * Removed an item associated with the passed key from the collection.
     * @param {String} key The key of the item to remove.
     * @return {Object/Boolean} The item removed or `false` if no item was removed.
     */
    removeAtKey: function(key){
        return this.removeAt(this.indexOfKey(key));
    },

    /**
     * Returns the number of items in the collection.
     * @return {Number} the number of items in the collection.
     */
    getCount: function(){
        return this.length;
    },

    /**
     * Returns index within the collection of the passed Object.
     * @param {Object} o The item to find the index of.
     * @return {Number} index of the item. Returns -1 if not found.
     */
    indexOf: function(o){
        return Ext.Array.indexOf(this.items, o);
    },

    /**
     * Returns index within the collection of the passed key.
     * @param {String} key The key to find the index of.
     * @return {Number} The index of the key.
     */
    indexOfKey: function(key){
        return Ext.Array.indexOf(this.keys, key);
    },

    /**
     * Returns the item associated with the passed key OR index.
     * Key has priority over index.  This is the equivalent
     * of calling {@link #getByKey} first, then if nothing matched calling {@link #getAt}.
     * @param {String/Number} key The key or index of the item.
     * @return {Object} If the item is found, returns the item.  If the item was not found, returns `undefined`.
     * If an item was found, but is a Class, returns `null`.
     */
    get: function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null; // for prototype!
    },

    /**
     * Returns the item at the specified index.
     * @param {Number} index The index of the item.
     * @return {Object} The item at the specified index.
     */
    getAt: function(index) {
        return this.items[index];
    },

    /**
     * Returns the item associated with the passed key.
     * @param {String/Number} key The key of the item.
     * @return {Object} The item associated with the passed key.
     */
    getByKey: function(key) {
        return this.map[key];
    },

    /**
     * Returns `true` if the collection contains the passed Object as an item.
     * @param {Object} o  The Object to look for in the collection.
     * @return {Boolean} `true` if the collection contains the Object as an item.
     */
    contains: function(o){
        return Ext.Array.contains(this.items, o);
    },

    /**
     * Returns `true` if the collection contains the passed Object as a key.
     * @param {String} key The key to look for in the collection.
     * @return {Boolean} `true` if the collection contains the Object as a key.
     */
    containsKey: function(key){
        return typeof this.map[key] != 'undefined';
    },

    /**
     * Removes all items from the collection.  Fires the `{@link #event-clear}` event when complete.
     */
    clear: function(){
        var me = this;

        me.length = 0;
        me.items = [];
        me.keys = [];
        me.map = {};
        me.fireEvent('clear');
    },

    /**
     * Returns the first item in the collection.
     * @return {Object} the first item in the collection..
     */
    first: function() {
        return this.items[0];
    },

    /**
     * Returns the last item in the collection.
     * @return {Object} the last item in the collection..
     */
    last: function() {
        return this.items[this.length - 1];
    },

    /**
     * Collects all of the values of the given property and returns their sum.
     * @param {String} property The property to sum by.
     * @param {String} [root] Optional 'root' property to extract the first argument from. This is used mainly when
     * summing fields in records, where the fields are all stored inside the `data` object
     * @param {Number} [start=0] (optional) The record index to start at.
     * @param {Number} [end=-1] (optional) The record index to end at.
     * @return {Number} The total
     */
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum    = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : length - 1;

        for (i = start; i <= end; i++) {
            sum += values[i];
        }

        return sum;
    },

    /**
     * Collects unique values of a particular property in this MixedCollection.
     * @param {String} property The property to collect on.
     * @param {String} [root] Optional 'root' property to extract the first argument from. This is used mainly when
     * summing fields in records, where the fields are all stored inside the `data` object.
     * @param {Boolean} [allowNull] Pass `true` to allow `null`, `undefined`, or empty string values.
     * @return {Array} The unique values.
     */
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits   = {},
            unique = [],
            value, strValue, i;

        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }

        return unique;
    },

    /**
     * @private
     * Extracts all of the given property values from the items in the MixedCollection. Mainly used as a supporting method for
     * functions like `sum()` and `collect()`.
     * @param {String} property The property to extract.
     * @param {String} [root] Optional 'root' property to extract the first argument from. This is used mainly when
     * extracting field data from Model instances, where the fields are stored inside the `data` object.
     * @return {Array} The extracted values.
     */
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    /**
     * Returns a range of items in this collection.
     * @param {Number} [start=0] The starting index.
     * @param {Number} [end=-1] The ending index.
     * @return {Array} An array of items
     */
    getRange: function(start, end){
        var me = this,
            items = me.items,
            range = [],
            i;

        if (items.length < 1) {
            return range;
        }

        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
        if (start <= end) {
            for (i = start; i <= end; i++) {
                range[range.length] = items[i];
            }
        } else {
            for (i = start; i >= end; i--) {
                range[range.length] = items[i];
            }
        }
        return range;
    },

    /**
     * Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single
     * property/value pair with optional parameters for substring matching and case sensitivity. See
     * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively,
     * MixedCollection can be easily filtered by property like this:
     *
     *     // create a simple store with a few people defined
     *     var people = new Ext.util.MixedCollection();
     *     people.addAll([
     *         {id: 1, age: 25, name: 'Ed'},
     *         {id: 2, age: 24, name: 'Tommy'},
     *         {id: 3, age: 24, name: 'Arne'},
     *         {id: 4, age: 26, name: 'Aaron'}
     *     ]);
     *
     *     // a new MixedCollection containing only the items where age == 24
     *     var middleAged = people.filter('age', 24);
     *
     * @param {Ext.util.Filter[]/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects
     * @param {String/RegExp} value Either string that the property values
     * should start with or a RegExp to test against the property.
     * @param {Boolean} anyMatch (optional) `true` to match any part of the string, not just the beginning
     * @param {Boolean} [caseSensitive=false] (optional) `true` for case sensitive comparison.
     * @return {Ext.util.MixedCollection} The new filtered collection
     */
    filter: function(property, value, anyMatch, caseSensitive) {
        var filters = [],
            filterFn;

        //support for the simple case of filtering by property/value
        if (Ext.isString(property)) {
            filters.push(Ext.create('Ext.util.Filter', {
                property     : property,
                value        : value,
                anyMatch     : anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }

        //at this point we have an array of zero or more Ext.util.Filter objects to filter with,
        //so here we construct a function that combines these filters by ANDing them together
        filterFn = function(record) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.getFilterFn(),
                    scope  = filter.getScope();

                isMatch = isMatch && fn.call(scope, record);
            }

            return isMatch;
        };

        return this.filterBy(filterFn);
    },

    /**
     * Filter by a function. Returns a _new_ collection that has been filtered.
     * The passed function will be called with each object in the collection.
     * If the function returns `true`, the value is included otherwise it is filtered.
     * @param {Function} fn The function to be called, it will receive the args `o` (the object), `k` (the key)
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to this MixedCollection.
     * @return {Ext.util.MixedCollection} The new filtered collection.
     */
    filterBy: function(fn, scope) {
        var me = this,
            newMC  = new this.self(),
            keys   = me.keys,
            items  = me.items,
            length = items.length,
            i;

        newMC.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }

        return newMC;
    },

    /**
     * Finds the index of the first matching object in this collection by a specific property/value.
     * @param {String} property The name of a property on your objects.
     * @param {String/RegExp} value A string that the property values.
     * should start with or a RegExp to test against the property.
     * @param {Number} [start=0] (optional) The index to start searching at.
     * @param {Boolean} anyMatch (optional) `true` to match any part of the string, not just the beginning.
     * @param {Boolean} caseSensitive (optional) `true` for case sensitive comparison.
     * @return {Number} The matched index or -1.
     */
    findIndex: function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    /**
     * Find the index of the first matching object in this collection by a function.
     * If the function returns `true` it is considered a match.
     * @param {Function} fn The function to be called, it will receive the args `o` (the object), `k` (the key).
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to this MixedCollection.
     * @param {Number} [start=0] (optional) The index to start searching at.
     * @return {Number} The matched index or -1.
     */
    findIndexBy: function(fn, scope, start){
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },

    /**
     * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
     * and by Ext.data.Store#filter
     * @private
     * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
     * @param {Boolean} [anyMatch=false] `true` to allow any match - no regex start/end line anchors will be added.
     * @param {Boolean} [caseSensitive=false] `true` to make the regex case sensitive (adds 'i' switch to regex).
     * @param {Boolean} [exactMatch=false] `true` to force exact match (^ and $ characters added to the regex). Ignored if `anyMatch` is `true`.
     */
    createValueMatcher: function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { // not a regex
            var er = Ext.String.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
        }
        return value;
    },

    /**
     * Creates a shallow copy of this collection.
     * @return {Ext.util.MixedCollection}
     */
    clone: function() {
        var me = this,
            copy = new this.self(),
            keys = me.keys,
            items = me.items,
            i = 0,
            len = items.length;

        for(; i < len; i++){
            copy.add(keys[i], items[i]);
        }
        copy.getKey = me.getKey;
        return copy;
    }
});