exports.mixin = function(_){

  var BIND_FLAG = 1,
      BIND_KEY_FLAG = 2,
      CURRY_BOUND_FLAG = 4,
      CURRY_FLAG = 8,
      CURRY_RIGHT_FLAG = 16,
      PARTIAL_FLAG = 32,
      PARTIAL_RIGHT_FLAG = 64,
      ARY_FLAG = 128,
      REARG_FLAG = 256,
      FLIP_FLAG = 512;

    /**
     * Creates a function that accepts arguments of `func` and either invokes
     * `func` returning its result, if at least `arity` number of arguments have
     * been provided, or returns a function that accepts the remaining `func`
     * arguments, and so on. The arity of `func` may be specified if `func.length`
     * is not sufficient.
     *
     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
     * may be used as a placeholder for provided arguments.
     *
     * **Note:** This method doesn't set the "length" property of curried functions.
     *
     * @static
     * @memberOf _
     * @category Function
     * @param {Function} func The function to curry.
     * @param {number} [arity=func.length] The arity of `func`.
     * @param- {Object} [guard] Enables use as an iteratee for functions like `_.map`.
     * @returns {Function} Returns the new curried function.
     * @example
     *
     * var abc = function(a, b, c) {
     *   return [a, b, c];
     * };
     *
     * var curried = _.curry(abc);
     *
     * curried(1)(2)(3);
     * // => [1, 2, 3]
     *
     * curried(1, 2)(3);
     * // => [1, 2, 3]
     *
     * curried(1, 2, 3);
     * // => [1, 2, 3]
     *
     * // Curried with placeholders.
     * curried(1)(_, 3)(2);
     * // => [1, 2, 3]
     */
    function curry(func, arity, guard) {
      arity = guard ? undefined : arity;
      var result = createWrapper(func, CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
      result.placeholder = curry.placeholder;
      return result;
    }

    /**
     * lodash 4.0.2 (Custom Build) <https://lodash.com/>
     * Build: `lodash modularize exports="npm" -o ./`
     * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
     * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
     * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     * Available under MIT license <https://lodash.com/license>
     */

    /** Used as the `TypeError` message for "Functions" methods. */
    var FUNC_ERROR_TEXT = 'Expected a function';

    /** Used as the internal argument placeholder. */
    var PLACEHOLDER = '__lodash_placeholder__';

    /** Used to compose bitmasks for wrapper metadata. */
    var BIND_FLAG = 1,
        BIND_KEY_FLAG = 2,
        CURRY_BOUND_FLAG = 4,
        CURRY_FLAG = 8,
        CURRY_RIGHT_FLAG = 16,
        PARTIAL_FLAG = 32,
        PARTIAL_RIGHT_FLAG = 64,
        ARY_FLAG = 128,
        FLIP_FLAG = 512;

    /** Used as references for various `Number` constants. */
    var INFINITY = 1 / 0,
        MAX_SAFE_INTEGER = 9007199254740991,
        MAX_INTEGER = 1.7976931348623157e+308,
        NAN = 0 / 0;

    /** `Object#toString` result references. */
    var funcTag = '[object Function]',
        genTag = '[object GeneratorFunction]';

    /** Used to match leading and trailing whitespace. */
    var reTrim = /^\s+|\s+$/g;

    /** Used to detect bad signed hexadecimal string values. */
    var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

    /** Used to detect binary string values. */
    var reIsBinary = /^0b[01]+$/i;

    /** Used to detect octal string values. */
    var reIsOctal = /^0o[0-7]+$/i;

    /** Used to detect unsigned integer values. */
    var reIsUint = /^(?:0|[1-9]\d*)$/;

    /** Used to determine if values are of the language type `Object`. */
    var objectTypes = {
      'function': true,
      'object': true
    };

    /** Built-in method references without a dependency on `root`. */
    var freeParseInt = parseInt;

    /** Detect free variable `exports`. */
    var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
      ? exports
      : undefined;

    /** Detect free variable `module`. */
    var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
      ? module
      : undefined;

    /** Detect free variable `global` from Node.js. */
    var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);

    /** Detect free variable `self`. */
    var freeSelf = checkGlobal(objectTypes[typeof self] && self);

    /** Detect free variable `window`. */
    var freeWindow = checkGlobal(objectTypes[typeof window] && window);

    /** Detect `this` as the global object. */
    var thisGlobal = checkGlobal(objectTypes[typeof this] && this);

    /**
     * Used as a reference to the global object.
     *
     * The `this` value is used if it's the global object to avoid Greasemonkey's
     * restricted `window` object, otherwise the `window` object is used.
     */
    var root = freeGlobal ||
      ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
        freeSelf || thisGlobal || Function('return this')();

    /**
     * A faster alternative to `Function#apply`, this function invokes `func`
     * with the `this` binding of `thisArg` and the arguments of `args`.
     *
     * @private
     * @param {Function} func The function to invoke.
     * @param {*} thisArg The `this` binding of `func`.
     * @param {...*} args The arguments to invoke `func` with.
     * @returns {*} Returns the result of `func`.
     */
    function apply(func, thisArg, args) {
      var length = args.length;
      switch (length) {
        case 0: return func.call(thisArg);
        case 1: return func.call(thisArg, args[0]);
        case 2: return func.call(thisArg, args[0], args[1]);
        case 3: return func.call(thisArg, args[0], args[1], args[2]);
      }
      return func.apply(thisArg, args);
    }

    /**
     * Checks if `value` is a global object.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {null|Object} Returns `value` if it's a global object, else `null`.
     */
    function checkGlobal(value) {
      return (value && value.Object === Object) ? value : null;
    }

    /**
     * Gets the number of `placeholder` occurrences in `array`.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {*} placeholder The placeholder to search for.
     * @returns {number} Returns the placeholder count.
     */
    function countHolders(array, placeholder) {
      var length = array.length,
          result = 0;

      while (length--) {
        if (array[length] === placeholder) {
          result++;
        }
      }
      return result;
    }

    /**
     * Checks if `value` is a valid array-like index.
     *
     * @private
     * @param {*} value The value to check.
     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
     */
    function isIndex(value, length) {
      value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
      length = length == null ? MAX_SAFE_INTEGER : length;
      return value > -1 && value % 1 == 0 && value < length;
    }

    /**
     * Replaces all `placeholder` elements in `array` with an internal placeholder
     * and returns an array of their indexes.
     *
     * @private
     * @param {Array} array The array to modify.
     * @param {*} placeholder The placeholder to replace.
     * @returns {Array} Returns the new array of placeholder indexes.
     */
    function replaceHolders(array, placeholder) {
      var index = -1,
          length = array.length,
          resIndex = 0,
          result = [];

      while (++index < length) {
        var value = array[index];
        if (value === placeholder || value === PLACEHOLDER) {
          array[index] = PLACEHOLDER;
          result[resIndex++] = index;
        }
      }
      return result;
    }

    /** Used for built-in method references. */
    var objectProto = Object.prototype;

    /**
     * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
     * of values.
     */
    var objectToString = objectProto.toString;

    /** Built-in value references. */
    var objectCreate = Object.create;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeMax = Math.max,
        nativeMin = Math.min;

    /**
     * The base implementation of `_.create` without support for assigning
     * properties to the created object.
     *
     * @private
     * @param {Object} prototype The object to inherit from.
     * @returns {Object} Returns the new object.
     */
    function baseCreate(proto) {
      return isObject(proto) ? objectCreate(proto) : {};
    }

    /**
     * Creates an array that is the composition of partially applied arguments,
     * placeholders, and provided arguments into a single array of arguments.
     *
     * @private
     * @param {Array|Object} args The provided arguments.
     * @param {Array} partials The arguments to prepend to those provided.
     * @param {Array} holders The `partials` placeholder indexes.
     * @params {boolean} [isCurried] Specify composing for a curried function.
     * @returns {Array} Returns the new array of composed arguments.
     */
    function composeArgs(args, partials, holders, isCurried) {
      var argsIndex = -1,
          argsLength = args.length,
          holdersLength = holders.length,
          leftIndex = -1,
          leftLength = partials.length,
          rangeLength = nativeMax(argsLength - holdersLength, 0),
          result = Array(leftLength + rangeLength),
          isUncurried = !isCurried;

      while (++leftIndex < leftLength) {
        result[leftIndex] = partials[leftIndex];
      }
      while (++argsIndex < holdersLength) {
        if (isUncurried || argsIndex < argsLength) {
          result[holders[argsIndex]] = args[argsIndex];
        }
      }
      while (rangeLength--) {
        result[leftIndex++] = args[argsIndex++];
      }
      return result;
    }

    /**
     * This function is like `composeArgs` except that the arguments composition
     * is tailored for `_.partialRight`.
     *
     * @private
     * @param {Array|Object} args The provided arguments.
     * @param {Array} partials The arguments to append to those provided.
     * @param {Array} holders The `partials` placeholder indexes.
     * @params {boolean} [isCurried] Specify composing for a curried function.
     * @returns {Array} Returns the new array of composed arguments.
     */
    function composeArgsRight(args, partials, holders, isCurried) {
      var argsIndex = -1,
          argsLength = args.length,
          holdersIndex = -1,
          holdersLength = holders.length,
          rightIndex = -1,
          rightLength = partials.length,
          rangeLength = nativeMax(argsLength - holdersLength, 0),
          result = Array(rangeLength + rightLength),
          isUncurried = !isCurried;

      while (++argsIndex < rangeLength) {
        result[argsIndex] = args[argsIndex];
      }
      var offset = argsIndex;
      while (++rightIndex < rightLength) {
        result[offset + rightIndex] = partials[rightIndex];
      }
      while (++holdersIndex < holdersLength) {
        if (isUncurried || argsIndex < argsLength) {
          result[offset + holders[holdersIndex]] = args[argsIndex++];
        }
      }
      return result;
    }

    /**
     * Copies the values of `source` to `array`.
     *
     * @private
     * @param {Array} source The array to copy values from.
     * @param {Array} [array=[]] The array to copy values to.
     * @returns {Array} Returns `array`.
     */
    function copyArray(source, array) {
      var index = -1,
          length = source.length;

      array || (array = Array(length));
      while (++index < length) {
        array[index] = source[index];
      }
      return array;
    }

    /**
     * Creates a function that wraps `func` to invoke it with the optional `this`
     * binding of `thisArg`.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper` for more details.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createBaseWrapper(func, bitmask, thisArg) {
      var isBind = bitmask & BIND_FLAG,
          Ctor = createCtorWrapper(func);

      function wrapper() {
        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
        return fn.apply(isBind ? thisArg : this, arguments);
      }
      return wrapper;
    }

    /**
     * Creates a function that produces an instance of `Ctor` regardless of
     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
     *
     * @private
     * @param {Function} Ctor The constructor to wrap.
     * @returns {Function} Returns the new wrapped function.
     */
    function createCtorWrapper(Ctor) {
      return function() {
        // Use a `switch` statement to work with class constructors.
        // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
        // for more details.
        var args = arguments;
        switch (args.length) {
          case 0: return new Ctor;
          case 1: return new Ctor(args[0]);
          case 2: return new Ctor(args[0], args[1]);
          case 3: return new Ctor(args[0], args[1], args[2]);
          case 4: return new Ctor(args[0], args[1], args[2], args[3]);
          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
        }
        var thisBinding = baseCreate(Ctor.prototype),
            result = Ctor.apply(thisBinding, args);

        // Mimic the constructor's `return` behavior.
        // See https://es5.github.io/#x13.2.2 for more details.
        return isObject(result) ? result : thisBinding;
      };
    }

    /**
     * Creates a function that wraps `func` to enable currying.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper` for more details.
     * @param {number} arity The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createCurryWrapper(func, bitmask, arity) {
      var Ctor = createCtorWrapper(func);

      function wrapper() {
        var length = arguments.length,
            args = Array(length),
            index = length,
            placeholder = getPlaceholder(wrapper);

        while (index--) {
          args[index] = arguments[index];
        }
        var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
          ? []
          : replaceHolders(args, placeholder);

        length -= holders.length;
        if (length < arity) {
          return createRecurryWrapper(
            func, bitmask, createHybridWrapper, wrapper.placeholder, undefined,
            args, holders, undefined, undefined, arity - length);
        }
        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
        return apply(fn, this, args);
      }
      return wrapper;
    }

    /**
     * Creates a function that wraps `func` to invoke it with optional `this`
     * binding of `thisArg`, partial application, and currying.
     *
     * @private
     * @param {Function|string} func The function or method name to wrap.
     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper` for more details.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to prepend to those provided to the new function.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
      var isAry = bitmask & ARY_FLAG,
          isBind = bitmask & BIND_FLAG,
          isBindKey = bitmask & BIND_KEY_FLAG,
          isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
          isFlip = bitmask & FLIP_FLAG,
          Ctor = isBindKey ? undefined : createCtorWrapper(func);

      function wrapper() {
        var length = arguments.length,
            index = length,
            args = Array(length);

        while (index--) {
          args[index] = arguments[index];
        }
        if (isCurried) {
          var placeholder = getPlaceholder(wrapper),
              holdersCount = countHolders(args, placeholder);
        }
        if (partials) {
          args = composeArgs(args, partials, holders, isCurried);
        }
        if (partialsRight) {
          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
        }
        length -= holdersCount;
        if (isCurried && length < arity) {
          var newHolders = replaceHolders(args, placeholder);
          return createRecurryWrapper(
            func, bitmask, createHybridWrapper, wrapper.placeholder, thisArg,
            args, newHolders, argPos, ary, arity - length
          );
        }
        var thisBinding = isBind ? thisArg : this,
            fn = isBindKey ? thisBinding[func] : func;

        length = args.length;
        if (argPos) {
          args = reorder(args, argPos);
        } else if (isFlip && length > 1) {
          args.reverse();
        }
        if (isAry && ary < length) {
          args.length = ary;
        }
        if (this && this !== root && this instanceof wrapper) {
          fn = Ctor || createCtorWrapper(fn);
        }
        return fn.apply(thisBinding, args);
      }
      return wrapper;
    }

    /**
     * Creates a function that wraps `func` to invoke it with the optional `this`
     * binding of `thisArg` and the `partials` prepended to those provided to
     * the wrapper.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper` for more details.
     * @param {*} thisArg The `this` binding of `func`.
     * @param {Array} partials The arguments to prepend to those provided to the new function.
     * @returns {Function} Returns the new wrapped function.
     */
    function createPartialWrapper(func, bitmask, thisArg, partials) {
      var isBind = bitmask & BIND_FLAG,
          Ctor = createCtorWrapper(func);

      function wrapper() {
        var argsIndex = -1,
            argsLength = arguments.length,
            leftIndex = -1,
            leftLength = partials.length,
            args = Array(leftLength + argsLength),
            fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;

        while (++leftIndex < leftLength) {
          args[leftIndex] = partials[leftIndex];
        }
        while (argsLength--) {
          args[leftIndex++] = arguments[++argsIndex];
        }
        return apply(fn, isBind ? thisArg : this, args);
      }
      return wrapper;
    }

    /**
     * Creates a function that wraps `func` to continue currying.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper` for more details.
     * @param {Function} wrapFunc The function to create the `func` wrapper.
     * @param {*} placeholder The placeholder value.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to prepend to those provided to the new function.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createRecurryWrapper(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
      var isCurry = bitmask & CURRY_FLAG,
          newArgPos = argPos ? copyArray(argPos) : undefined,
          newHolders = isCurry ? holders : undefined,
          newHoldersRight = isCurry ? undefined : holders,
          newPartials = isCurry ? partials : undefined,
          newPartialsRight = isCurry ? undefined : partials;

      bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
      bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);

      if (!(bitmask & CURRY_BOUND_FLAG)) {
        bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
      }

      var result = wrapFunc(func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, newArgPos, ary, arity);
      result.placeholder = placeholder;
      return result;
    }

    /**
     * Creates a function that either curries or invokes `func` with optional
     * `this` binding and partially applied arguments.
     *
     * @private
     * @param {Function|string} func The function or method name to wrap.
     * @param {number} bitmask The bitmask of wrapper flags.
     *  The bitmask may be composed of the following flags:
     *     1 - `_.bind`
     *     2 - `_.bindKey`
     *     4 - `_.curry` or `_.curryRight` of a bound function
     *     8 - `_.curry`
     *    16 - `_.curryRight`
     *    32 - `_.partial`
     *    64 - `_.partialRight`
     *   128 - `_.rearg`
     *   256 - `_.ary`
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {Array} [partials] The arguments to be partially applied.
     * @param {Array} [holders] The `partials` placeholder indexes.
     * @param {Array} [argPos] The argument positions of the new function.
     * @param {number} [ary] The arity cap of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new wrapped function.
     */
    function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
      var isBindKey = bitmask & BIND_KEY_FLAG;
      if (!isBindKey && typeof func != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      var length = partials ? partials.length : 0;
      if (!length) {
        bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
        partials = holders = undefined;
      }
      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
      arity = arity === undefined ? arity : toInteger(arity);
      length -= holders ? holders.length : 0;

      if (bitmask & PARTIAL_RIGHT_FLAG) {
        var partialsRight = partials,
            holdersRight = holders;

        partials = holders = undefined;
      }

      var newData = [
        func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
        argPos, ary, arity
      ];

      func = newData[0];
      bitmask = newData[1];
      thisArg = newData[2];
      partials = newData[3];
      holders = newData[4];
      arity = newData[9] = newData[9] == null
        ? (isBindKey ? 0 : func.length)
        : nativeMax(newData[9] - length, 0);

      if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {
        bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);
      }
      if (!bitmask || bitmask == BIND_FLAG) {
        var result = createBaseWrapper(func, bitmask, thisArg);
      } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {
        result = createCurryWrapper(func, bitmask, arity);
      } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {
        result = createPartialWrapper(func, bitmask, thisArg, partials);
      } else {
        result = createHybridWrapper.apply(undefined, newData);
      }
      return result;
    }

    /**
     * Gets the argument placeholder value for `func`.
     *
     * @private
     * @param {Function} func The function to inspect.
     * @returns {*} Returns the placeholder value.
     */
    function getPlaceholder(func) {
      var object = func;
      return object.placeholder;
    }

    /**
     * Reorder `array` according to the specified indexes where the element at
     * the first index is assigned as the first element, the element at
     * the second index is assigned as the second element, and so on.
     *
     * @private
     * @param {Array} array The array to reorder.
     * @param {Array} indexes The arranged array indexes.
     * @returns {Array} Returns `array`.
     */
    function reorder(array, indexes) {
      var arrLength = array.length,
          length = nativeMin(indexes.length, arrLength),
          oldArray = copyArray(array);

      while (length--) {
        var index = indexes[length];
        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
      }
      return array;
    }

    /**
     * Checks if `value` is classified as a `Function` object.
     *
     * @static
     * @memberOf _
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
     * @example
     *
     * _.isFunction(_);
     * // => true
     *
     * _.isFunction(/abc/);
     * // => false
     */
    function isFunction(value) {
      // The use of `Object#toString` avoids issues with the `typeof` operator
      // in Safari 8 which returns 'object' for typed array and weak map constructors,
      // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
      var tag = isObject(value) ? objectToString.call(value) : '';
      return tag == funcTag || tag == genTag;
    }

    /**
     * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
     *
     * @static
     * @memberOf _
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
     * @example
     *
     * _.isObject({});
     * // => true
     *
     * _.isObject([1, 2, 3]);
     * // => true
     *
     * _.isObject(_.noop);
     * // => true
     *
     * _.isObject(null);
     * // => false
     */
    function isObject(value) {
      var type = typeof value;
      return !!value && (type == 'object' || type == 'function');
    }

    /**
     * Converts `value` to an integer.
     *
     * **Note:** This function is loosely based on [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
     *
     * @static
     * @memberOf _
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {number} Returns the converted integer.
     * @example
     *
     * _.toInteger(3);
     * // => 3
     *
     * _.toInteger(Number.MIN_VALUE);
     * // => 0
     *
     * _.toInteger(Infinity);
     * // => 1.7976931348623157e+308
     *
     * _.toInteger('3');
     * // => 3
     */
    function toInteger(value) {
      if (!value) {
        return value === 0 ? value : 0;
      }
      value = toNumber(value);
      if (value === INFINITY || value === -INFINITY) {
        var sign = (value < 0 ? -1 : 1);
        return sign * MAX_INTEGER;
      }
      var remainder = value % 1;
      return value === value ? (remainder ? value - remainder : value) : 0;
    }

    /**
     * Converts `value` to a number.
     *
     * @static
     * @memberOf _
     * @category Lang
     * @param {*} value The value to process.
     * @returns {number} Returns the number.
     * @example
     *
     * _.toNumber(3);
     * // => 3
     *
     * _.toNumber(Number.MIN_VALUE);
     * // => 5e-324
     *
     * _.toNumber(Infinity);
     * // => Infinity
     *
     * _.toNumber('3');
     * // => 3
     */
    function toNumber(value) {
      if (isObject(value)) {
        var other = isFunction(value.valueOf) ? value.valueOf() : value;
        value = isObject(other) ? (other + '') : other;
      }
      if (typeof value != 'string') {
        return value === 0 ? value : +value;
      }
      value = value.replace(reTrim, '');
      var isBinary = reIsBinary.test(value);
      return (isBinary || reIsOctal.test(value))
        ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
        : (reIsBadHex.test(value) ? NAN : +value);
    }

    _.curry = curry;
    _.curry.placeholder = _;
};


'use strict';

var request = require( './../lib' );

var opts = {
	'hostname': 'api.travis-ci.org',
	'pathname': '/builds/114143550/restart',

	// INSERT TOKEN HERE //
	'token': '<your_token_goes_here>'
};

request( opts, onResponse );

/**
* FUNCTION: onResponse( error, results )
*	Callback invoked upon receiving a response.
*
* @private
* @param {Error|Null} error - error or null
* @param {Object} results - response results
* @returns {Void}
*/
function onResponse( error, results ) {
	if ( error ) {
		throw new Error( error.message );
	}
	console.log( results );
}


"use strict";
/* tslint:disable:ban-types trailing-comma */

Object.defineProperty(exports, "__esModule", { value: true });
/**
 * Returns a method decorator taking a callback, which will be invoked
 * before the execution of the decorated function, *unless* the callback
 * returns `false`.
 *
 * @param fn a function to be decorated
 * @param callback a handler to be invoked beforehand
 */
function before(fn, callback) {
    return function () {
        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
            args[_key] = arguments[_key];
        }

        if (callback.apply(this, [fn.bind(this)].concat(args)) !== false) {
            return fn.apply(this, args);
        }
    };
}
exports.before = before;
exports.default = before;
//# sourceMappingURL=before.js.map


"use strict";

var Bluebird = require("bluebird"),
	fs = require("fs"),
	minimatch = require("minimatch"),
	path = require("path"),
	util = require("util");

var ignorePatterns = [],
	messages = {};

try {
	ignorePatterns = fs.readFileSync(".precommitignore", "utf8")
		.replace(/\r/g, "")
		.split(/\n/)
		.filter(function(v) {
			return !!v;
		});
} catch (e) {
	// empty
}

/**
 * Check if a file exists
 * @param {String} file
 * @returns {Boolean}
 */
function fileExists(file) {
	try {
		fs.statSync(file);
		return true;
	} catch (e) {
		return false;
	}
}

/**
 * Write output to the console
 * @param {String} data
 * @returns {*|Number}
 */
function write(data) {
	return process.stdout.write(data);
}

/**
 * Write output line to the console
 * @param {String} data
 * @returns {*|Number}
 */
function writeLine(data) {
	return process.stdout.write(data + "\n");
}

/**
 * Write an error to the errors buffer
 * @param {String} type
 * @param {String} file
 * @param {String} line
 * @param {String} message
 */
function writeError(type, file, line, message) {
	if (!messages[type]) {
		messages[type] = [];
	}
	messages[type].push({
		type : type,
		file : file,
		line : line,
		message : message
	});
}

/**
 * Flush the errors buffer
 */
function flushMessages() {
	Object.keys(messages)
		.forEach(function(type) {
			messages[type].forEach(function(msg) {
				writeLine(msg.type + " error: [" + msg.file + ":" + msg.line + "] " + msg.message);
			});
		});
}

/**
 * Get a JSON based config file
 * @param {String} file
 * @returns {Object}
 */
function getConfig(file) {
	var config = {},
		subConfig;
	try {
		config = JSON.parse(fs.readFileSync(file, "utf8"));
		if (config.extends) {
			subConfig = JSON.parse(fs.readFileSync(config.extends, "utf8"));
			util._extend(subConfig, config);
			delete subConfig.extends;
			config = subConfig;
		}
	} catch (e) {
		// empty
	}
	return config;
}

/**
 * Check if a filename matches an ignore pattern
 * @param {String} file
 * @returns {Boolean}
 */
function isIgnored(file) {
	return ignorePatterns.some(function(pattern) {
		return minimatch(file, pattern, {
			nocase : true,
			matchBase : true
		});
	});
}

/**
 * Replace non script content in a html file with empty lines
 * @param {String} src
 * @returns {String}
 */
function extractScripts(src) {
	var isInBlock = false,
		lines = [];

	src.replace(/\r/g, "").split("\n").forEach(function(l) {
		// we're at the end of the script tag
		if (l.indexOf("</script") > -1) {
			lines[lines.length] = "";
			isInBlock = false;
			return;
		}
		if (isInBlock) {
			lines[lines.length] = l;
		} else {
			lines[lines.length] = "";
		}
		if (l.indexOf("<script") > -1 && (l.indexOf("text/ng-template") === -1 && l.indexOf("text/html") === -1)) {
			isInBlock = true;
		}
	});
	return lines.join("\n").replace(/\{\$(\w+\.)*\w+\}/g, "{}");
}

/**
 * Replace non css content in a html file with empty lines
 * @param {String} src
 * @returns {String}
 */
function extractStyles(src) {
	var isInBlock = false,
		lines = [];

	src.replace(/\r/g, "").split("\n").forEach(function(l) {
		// we're at the end of the style tag
		if (l.indexOf("</style") > -1) {
			lines[lines.length] = "";
			isInBlock = false;
			return;
		}
		if (isInBlock) {
			lines[lines.length] = l;
		} else {
			lines[lines.length] = "";
		}
		if (l.indexOf("<style") > -1) {
			isInBlock = true;
		}
	});
	return lines.join("\n").replace(/\{\$(\w+\.)*\w+\}/g, "{}");
}

/**
 * Get file contents and file name from process.argv
 * @returns {Promise.<{filename: *, src}>|String|*}
 */
function read() {
	var filename = process.argv[2],
		src = fs.readFileSync(process.argv[3], "utf8");

	// attempt to modify any src passing through the pre-commit hook
	try {
		src = require(path.join(process.cwd(), ".git-hooks/pre-commit-plugins/pre-commit-modifier"))(filename, src);
	} catch (e) {
		// empty
	}
	// filename, src
	return Bluebird.resolve({
		filename : filename,
		src : src
	});
}

/**
 * Finish script
 * @param {Number} code
 */
function done(code) {
	setTimeout(function() {
		process.exit(code || 0);
	}, 4);
}

/**
 * Load all file type checker plugins
 * @param {String} extension
 * @throws {Error}
 * @returns {Function}
 */
function loadFileTypePlugin(extension) {
	return require(
		path.join(
			process.cwd(),
			".git-hooks/pre-commit-plugins/filetypes/file-" + extension
		)
	);
}

/**
 * Load all file checking plugins
 * @returns {Object}
 */
function loadFileCheckerPlugins() {
	var checkers = {};
	try {
		fs.readdirSync(path.join(process.cwd(), ".git-hooks/pre-commit-plugins/plugins"))
			.forEach(function(file) {
				var check = file.replace(/\.js$/, "");
				if (!(/\.js$/).test(file)) {
					return;
				}
				checkers[check] = require(path.join(process.cwd(), ".git-hooks/pre-commit-plugins/plugins", file));
			});
	} catch (e) {
		// empty
	}
	return checkers;
}

/**
 * Exports
 */
module.exports = {
	fileExists : fileExists,
	write : write,
	writeLine : writeLine,
	writeError : writeError,
	flushMessages : flushMessages,
	getConfig : getConfig,
	isIgnored : isIgnored,
	extractScripts : extractScripts,
	extractStyles : extractStyles,
	read : read,
	done : done,
	loadFileTypePlugin : loadFileTypePlugin,
	loadFileCheckerPlugins : loadFileCheckerPlugins
};


/**
 * @private
 */
Ext.define('Ext.fx.animation.Cube', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.cube',

    config: {
        /**
         * @cfg
         * @inheritdoc
         */
        before: {
//            'transform-style': 'preserve-3d'
        },

        after: {},

        /**
         * @cfg {String} direction The direction of which the slide animates
         * @accessor
         */
        direction: 'right',

        out: false
    },

//    getData: function() {
//        var to = this.getTo(),
//            from = this.getFrom(),
//            out  = this.getOut(),
//            direction  = this.getDirection(),
//            el = this.getElement(),
//            elW = el.getWidth(),
//            elH = el.getHeight(),
//            halfWidth = (elW / 2),
//            halfHeight = (elH / 2),
//            fromTransform = {},
//            toTransform = {},
//            originalFromTransform = {
//                rotateY: 0,
//                translateX: 0,
//                translateZ: 0
//            },
//            originalToTransform = {
//                rotateY: 90,
//                translateX: halfWidth,
//                translateZ: halfWidth
//            },
//            originalVerticalFromTransform = {
//                rotateX: 0,
//                translateY: 0,
//                translateZ: 0
//            },
//            originalVerticalToTransform = {
//                rotateX: 90,
//                translateY: halfHeight,
//                translateZ: halfHeight
//            },
//            tempTransform;
//
//        if (direction == "left" || direction == "right") {
//            if (out) {
//                toTransform = originalToTransform;
//                fromTransform = originalFromTransform;
//            } else {
//                toTransform = originalFromTransform;
//                fromTransform = originalToTransform;
//                fromTransform.rotateY *= -1;
//                fromTransform.translateX *= -1;
//            }
//
//            if (direction === 'right') {
//                tempTransform = fromTransform;
//                fromTransform = toTransform;
//                toTransform = tempTransform;
//            }
//        }
//
//        if (direction == "up" || direction == "down") {
//            if (out) {
//                toTransform = originalVerticalFromTransform;
//                fromTransform = {
//                    rotateX: -90,
//                    translateY: halfHeight,
//                    translateZ: halfHeight
//                };
//            } else {
//                fromTransform = originalVerticalFromTransform;
//                toTransform = {
//                    rotateX: 90,
//                    translateY: -halfHeight,
//                    translateZ: halfHeight
//                };
//            }
//
//            if (direction == "up") {
//                tempTransform = fromTransform;
//                fromTransform = toTransform;
//                toTransform = tempTransform;
//            }
//        }
//
//        from.set('transform', fromTransform);
//        to.set('transform', toTransform);
//
//        return this.callParent(arguments);
//    },

    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            before = this.getBefore(),
            after = this.getAfter(),
            out  = this.getOut(),
            direction  = this.getDirection(),
            el = this.getElement(),
            elW = el.getWidth(),
            elH = el.getHeight(),
            origin = out ? '100% 100%' : '0% 0%',
            fromOpacity = 1,
            toOpacity = 1,
            transformFrom = {
                rotateY: 0,
                translateZ: 0
            },
            transformTo = {
                rotateY: 0,
                translateZ: 0
            };

        if (direction == "left" || direction == "right") {
            if (out) {
                toOpacity = 0.5;
                transformTo.translateZ = elW;
                transformTo.rotateY = -90;
            } else {
                fromOpacity = 0.5;
                transformFrom.translateZ = elW;
                transformFrom.rotateY = 90;
            }
        }

        before['transform-origin'] = origin;
        after['transform-origin'] = null;

        to.set('transform', transformTo);
        from.set('transform', transformFrom);

        from.set('opacity', fromOpacity);
        to.set('opacity', toOpacity);

        return this.callParent(arguments);
    }
});


/**
 * Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:
 *
 *     Ext.define('Product', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 {name: 'id',      type: 'int'},
 *                 {name: 'user_id', type: 'int'},
 *                 {name: 'name',    type: 'string'}
 *             ]
 *         }
 *     });
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 {name: 'id',   type: 'int'},
 *                 {name: 'name', type: 'string'}
 *             ],
 *             // we can use the hasMany shortcut on the model to create a hasMany association
 *             hasMany: {model: 'Product', name: 'products'}
 *         }
 *     });
 *
 * Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives us a new
 * function on every User instance, in this case the function is called 'products' because that is the name we specified
 * in the association configuration above.
 *
 * This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load only
 * Products for the given model instance:
 *
 *     //first, we load up a User with id of 1
 *     var user = Ext.create('User', {id: 1, name: 'Ed'});
 *
 *     //the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
 *     //the created store is automatically scoped to the set of Products for the User with id of 1
 *     var products = user.products();
 *
 *     //we still have all of the usual Store functions, for example it's easy to add a Product for this User
 *     products.add({
 *         name: 'Another Product'
 *     });
 *
 *     //saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
 *     products.sync();
 *
 * The new Store is only instantiated the first time you call products() to conserve memory and processing time, though
 * calling products() a second time returns the same store instance.
 *
 * _Custom filtering_
 *
 * The Store is automatically furnished with a filter - by default this filter tells the store to only return records
 * where the associated model's foreign key matches the owner model's primary key. For example, if a User with ID = 100
 * hasMany Products, the filter loads only Products with user_id == 100.
 *
 * Sometimes we want to filter by another field - for example in the case of a Twitter search application we may have
 * models for Search and Tweet:
 *
 *     Ext.define('Search', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'id', 'query'
 *             ],
 *
 *             hasMany: {
 *                 model: 'Tweet',
 *                 name : 'tweets',
 *                 filterProperty: 'query'
 *             }
 *         }
 *     });
 *
 *     Ext.define('Tweet', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'id', 'text', 'from_user'
 *             ]
 *         }
 *     });
 *
 *     //returns a Store filtered by the filterProperty
 *     var store = new Search({query: 'Sencha Touch'}).tweets();
 *
 * The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
 * equivalent to this:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'Tweet',
 *         filters: [
 *             {
 *                 property: 'query',
 *                 value   : 'Sencha Touch'
 *             }
 *         ]
 *     });
 *
 * ###Further Reading
 * [Sencha Touch Models and Associations](../../../core_concepts/data/models.html)
 */
Ext.define('Ext.data.association.HasMany', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.HasManyAssociation',
    requires: ['Ext.util.Inflector'],

    alias: 'association.hasmany',

    config: {
        /**
         * @cfg {String} foreignKey
         * The name of the foreign key on the associated model that links it to the owner model. Defaults to the
         * lowercased name of the owner model plus "_id", e.g. an association with a model called Group hasMany Users
         * would create 'group_id' as the foreign key. When the remote store is loaded, the store is automatically
         * filtered so that only records with a matching foreign key are included in the resulting child store. This can
         * be overridden by specifying the {@link #filterProperty}.
         *
         *     Ext.define('Group', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name'],
         *         hasMany: 'User'
         *     });
         *
         *     Ext.define('User', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name', 'group_id'], // refers to the id of the group that this user belongs to
         *         belongsTo: 'Group'
         *     });
         */
        foreignKey: undefined,

        /**
         * @cfg {String} name
         * The name of the function to create on the owner model to retrieve the child store. If not specified, the
         * pluralized name of the child model is used.
         *
         *     // This will create a users() method on any Group model instance
         *     Ext.define('Group', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name'],
         *         hasMany: 'User'
         *     });
         *     var group = new Group();
         *     console.log(group.users());
         *
         *     // The method to retrieve the users will now be getUserList
         *     Ext.define('Group', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name'],
         *         hasMany: {model: 'User', name: 'getUserList'}
         *     });
         *     var group = new Group();
         *     console.log(group.getUserList());
         */

        /**
         * @cfg {Object} store
         * Optional configuration object that will be passed to the generated Store. Defaults to an empty Object.
         */
        store: undefined,

        /**
         * @cfg {String} storeName
         * Optional The name of the store by which you can reference it on this class as a property.
         */
        storeName: undefined,

        /**
         * @cfg {String} filterProperty
         * Optionally overrides the default filter that is set up on the associated Store. If this is not set, a filter
         * is automatically created which filters the association based on the configured {@link #foreignKey}. See intro
         * docs for more details.
         */
        filterProperty: null,

        /**
         * @cfg {Boolean} autoLoad
         * `true` to automatically load the related store from a remote source when instantiated.
         */
        autoLoad: false,

        /**
         * @cfg {Boolean} autoSync
         * true to automatically synchronize the related store with the remote source
         */
        autoSync: false
    },

    constructor: function(config) {
        config = config || {};

        if (config.storeConfig) {
            // <debug>
            Ext.Logger.warn('storeConfig is deprecated on an association. Instead use the store configuration.');
            // </debug>
            config.store = config.storeConfig;
            delete config.storeConfig;
        }

        this.callParent([config]);
    },

    applyName: function(name) {
        if (!name) {
            name = Ext.util.Inflector.pluralize(this.getAssociatedName().toLowerCase());
        }
        return name;
    },

    applyStoreName: function(name) {
        if (!name) {
            name = this.getName() + 'Store';
        }
        return name;
    },

    applyForeignKey: function(foreignKey) {
        if (!foreignKey) {
            var inverse = this.getInverseAssociation();
            if (inverse) {
                foreignKey = inverse.getForeignKey();
            } else {
                foreignKey = this.getOwnerName().toLowerCase() + '_id';
            }
        }
        return foreignKey;
    },

    applyAssociationKey: function(associationKey) {
        if (!associationKey) {
            var associatedName = this.getAssociatedName();
            associationKey = Ext.util.Inflector.pluralize(associatedName[0].toLowerCase() + associatedName.slice(1));
        }
        return associationKey;
    },

    updateForeignKey: function(foreignKey, oldForeignKey) {
        var fields = this.getAssociatedModel().getFields(),
            field = fields.get(foreignKey);

        if (!field) {
            field = new Ext.data.Field({
                name: foreignKey
            });
            fields.add(field);
            fields.isDirty = true;
        }

        if (oldForeignKey) {
            field = fields.get(oldForeignKey);
            if (field) {
                fields.remove(field);
                fields.isDirty = true;
            }
        }
    },

    /**
     * @private
     * @deprecated as of v2.0.0 on an association. Instead use the store configuration.
     *
     * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
     * by the owner model's primary key - e.g. in a `hasMany` association where Group `hasMany` Users, this function
     * returns a Store configured to return the filtered set of a single Group's Users.
     * @return {Function} The store-generating function.
     */
    applyStore: function(storeConfig) {
        var me = this,
            associatedModel = me.getAssociatedModel(),
            storeName       = me.getStoreName(),
            foreignKey      = me.getForeignKey(),
            primaryKey      = me.getPrimaryKey(),
            filterProperty  = me.getFilterProperty(),
            autoLoad        = me.getAutoLoad(),
            autoSync        = me.getAutoSync();

        return function() {
            var record = this,
                config, filter, store,
                modelDefaults = {},
                listeners = {
                    addrecords: me.onAddRecords,
                    removerecords: me.onRemoveRecords,
                    scope: me
                };

            if (record[storeName] === undefined) {
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : record.get(filterProperty),
                        exactMatch: true
                    };
                } else {
                    filter = {
                        property  : foreignKey,
                        value     : record.get(primaryKey),
                        exactMatch: true
                    };
                }

                modelDefaults[foreignKey] = record.get(primaryKey);

                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : [filter],
                    remoteFilter : true,
                    autoSync     : autoSync,
                    modelDefaults: modelDefaults
                });

                store = record[storeName] = Ext.create('Ext.data.Store', config);
                store.boundTo = record;

                store.onAfter(listeners);

                if (autoLoad) {
                    record[storeName].load();
                }
            }

            return record[storeName];
        };
    },

    onAddRecords: function(store, records) {
        var ln = records.length,
            id = store.boundTo.getId(),
            i, record;

        for (i = 0; i < ln; i++) {
            record = records[i];
            record.set(this.getForeignKey(), id);
        }
        this.updateInverseInstances(store.boundTo);
    },

    onRemoveRecords: function(store, records) {
        var ln = records.length,
            i, record;
        for (i = 0; i < ln; i++) {
            record = records[i];
            record.set(this.getForeignKey(), null);
        }
    },

    updateStore: function(store) {
        this.getOwnerModel().prototype[this.getName()] = store;
    },

    /**
     * Read associated data
     * @private
     * @param {Ext.data.Model} record The record we're writing to.
     * @param {Ext.data.reader.Reader} reader The reader for the associated model.
     * @param {Object} associationData The raw associated data.
     */
    read: function(record, reader, associationData) {
        var store = record[this.getName()](),
            records = reader.read(associationData).getRecords();

        store.add(records);
    },

    updateInverseInstances: function(record) {
        var store = record[this.getName()](),
            inverse = this.getInverseAssociation();

        //if the inverse association was found, set it now on each record we've just created
        if (inverse) {
            store.each(function(associatedRecord) {
                associatedRecord[inverse.getInstanceName()] = record;
            });
        }
    },

    getInverseAssociation: function() {
        var ownerName = this.getOwnerModel().modelName;

        //now that we've added the related records to the hasMany association, set the inverse belongsTo
        //association on each of them if it exists
        return this.getAssociatedModel().associations.findBy(function(assoc) {
            return assoc.getType().toLowerCase() === 'belongsto' && assoc.getAssociatedModel().modelName === ownerName;
        });
    }

    // <deprecated product=touch since=2.0>
}, function() {
    /**
     * @cfg {Object} storeConfig
     * @deprecated 2.0.0 Use `store` instead.
     */
    Ext.deprecateProperty(this, 'storeConfig', 'store');
    // </deprecated>
});


//@tag dom,core
//@define Ext.Element-all
//@define Ext.Element-style
//@require Ext.Element-position

/**
 * @class Ext.dom.Element
 */

Ext.dom.Element.addMembers({
    WIDTH: 'width',
    HEIGHT: 'height',
    MIN_WIDTH: 'min-width',
    MIN_HEIGHT: 'min-height',
    MAX_WIDTH: 'max-width',
    MAX_HEIGHT: 'max-height',
    TOP: 'top',
    RIGHT: 'right',
    BOTTOM: 'bottom',
    LEFT: 'left',
    /**
     * @property VISIBILITY
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use `visibility` to hide element.
     */
    VISIBILITY: 1,

    /**
     * @property DISPLAY
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use `display` to hide element.
     */
    DISPLAY: 2,

    /**
     * @property OFFSETS
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets to hide element.
     */
    OFFSETS: 3,

    SEPARATOR: '-',

    trimRe: /^\s+|\s+$/g,
    wordsRe: /\w/g,
    spacesRe: /\s+/,
    styleSplitRe: /\s*(?::|;)\s*/,
    transparentRe: /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
    classNameSplitRegex: /[\s]+/,

    borders: {
        t: 'border-top-width',
        r: 'border-right-width',
        b: 'border-bottom-width',
        l: 'border-left-width'
    },

    paddings: {
        t: 'padding-top',
        r: 'padding-right',
        b: 'padding-bottom',
        l: 'padding-left'
    },

    margins: {
        t: 'margin-top',
        r: 'margin-right',
        b: 'margin-bottom',
        l: 'margin-left'
    },

    /**
     * @property {String} defaultUnit
     * The default unit to append to CSS values where a unit isn't provided.
     */
    defaultUnit: "px",

    isSynchronized: false,

    /**
     * @private
     */
    synchronize: function() {
        var dom = this.dom,
            hasClassMap = {},
            className = dom.className,
            classList, i, ln, name;

        if (className.length > 0) {
            classList = dom.className.split(this.classNameSplitRegex);

            for (i = 0, ln = classList.length; i < ln; i++) {
                name = classList[i];
                hasClassMap[name] = true;
            }
        }
        else {
            classList = [];
        }

        this.classList = classList;

        this.hasClassMap = hasClassMap;

        this.isSynchronized = true;

        return this;
    },

    /**
     * Adds the given CSS class(es) to this Element.
     * @param {String} names The CSS class(es) to add to this element.
     * @param {String} [prefix] (optional) Prefix to prepend to each class.
     * @param {String} [suffix] (optional) Suffix to append to each class.
     */
    addCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRe);
        }

        for (i = 0, ln = names.length; i < ln; i++) {
            name = prefix + names[i] + suffix;

            if (!map[name]) {
                map[name] = true;
                classList.push(name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    /**
     * Removes the given CSS class(es) from this Element.
     * @param {String} names The CSS class(es) to remove from this element.
     * @param {String} [prefix=''] Prefix to prepend to each class to be removed.
     * @param {String} [suffix=''] Suffix to append to each class to be removed.
     */
    removeCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }

        if (!suffix) {
            suffix = '';
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRe);
        }

        for (i = 0, ln = names.length; i < ln; i++) {
            name = prefix + names[i] + suffix;

            if (map[name]) {
                delete map[name];
                Ext.Array.remove(classList, name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    /**
     * Replaces a CSS class on the element with another.
     * If the old name does not exist, the new name will simply be added.
     * @param {String} oldName The CSS class to replace.
     * @param {String} newName The replacement CSS class.
     * @param {String} [prefix=''] Prefix to prepend to each class to be replaced.
     * @param {String} [suffix=''] Suffix to append to each class to be replaced.
     * @return {Ext.dom.Element} this
     */
    replaceCls: function(oldName, newName, prefix, suffix) {
        if (!oldName && !newName) {
            return this;
        }

        oldName = oldName || [];
        newName = newName || [];

        if (!this.isSynchronized) {
            this.synchronize();
        }

        if (!suffix) {
            suffix = '';
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof oldName == 'string') {
            oldName = oldName.split(this.spacesRe);
        }
        if (typeof newName == 'string') {
            newName = newName.split(this.spacesRe);
        }

        for (i = 0, ln = oldName.length; i < ln; i++) {
            name = prefix + oldName[i] + suffix;

            if (map[name]) {
                delete map[name];
                Ext.Array.remove(classList, name);
            }
        }

        for (i = 0, ln = newName.length; i < ln; i++) {
            name = prefix + newName[i] + suffix;

            if (!map[name]) {
                map[name] = true;
                classList.push(name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    /**
     * Checks if the specified CSS class exists on this element's DOM node.
     * @param {String} name The CSS class to check for.
     * @return {Boolean} `true` if the class exists, else `false`.
     */
    hasCls: function(name) {
        if (!this.isSynchronized) {
            this.synchronize();
        }

        return this.hasClassMap.hasOwnProperty(name);
    },

    /**
     * Sets the specified CSS class on this element's DOM node.
     * @param {String/Array} className The CSS class to set on this element.
     */
    setCls: function(className) {
        var map = this.hasClassMap,
            i, ln, name;

        if (typeof className == 'string') {
            className = className.split(this.spacesRe);
        }

        for (i = 0, ln = className.length; i < ln; i++) {
            name = className[i];
            if (!map[name]) {
                map[name] = true;
            }
        }

        this.classList = className.slice();
        this.dom.className = className.join(' ');
    },

    /**
     * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
     * @param {String} className The CSS class to toggle.
     * @return {Ext.dom.Element} this
     */
    toggleCls: function(className, force){
        if (typeof force !== 'boolean') {
            force = !this.hasCls(className);
        }

   		return (force) ? this.addCls(className) : this.removeCls(className);
   	},

    /**
     * @private
     * @param {String} firstClass
     * @param {String} secondClass
     * @param {Boolean} flag
     * @param {String} prefix
     * @return {Mixed}
     */
    swapCls: function(firstClass, secondClass, flag, prefix) {
        if (flag === undefined) {
            flag = true;
        }

        var addedClass = flag ? firstClass : secondClass,
            removedClass = flag ? secondClass : firstClass;

        if (removedClass) {
            this.removeCls(prefix ? prefix + '-' + removedClass : removedClass);
        }

        if (addedClass) {
            this.addCls(prefix ? prefix + '-' + addedClass : addedClass);
        }

        return this;
    },

    /**
     * Set the width of this Element.
     * @param {Number/String} width The new width.
     * @return {Ext.dom.Element} this
     */
    setWidth: function(width) {
        return this.setLengthValue(this.WIDTH, width);
    },

    /**
     * Set the height of this Element.
     * @param {Number/String} height The new height.
     * @return {Ext.dom.Element} this
     */
    setHeight: function(height) {
        return this.setLengthValue(this.HEIGHT, height);
    },

    /**
     * Set the size of this Element.
     *
     * @param {Number/String} width The new width. This may be one of:
     *
     * - A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).
     * - A String used to set the CSS width style. Animation may **not** be used.
     * - A size object in the format `{width: widthValue, height: heightValue}`.
     *
     * @param {Number/String} height The new height. This may be one of:
     *
     * - A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).
     * - A String used to set the CSS height style. Animation may **not** be used.
     * @return {Ext.dom.Element} this
     */
    setSize: function(width, height) {
        if (Ext.isObject(width)) {
            // in case of object from getSize()
            height = width.height;
            width = width.width;
        }

        this.setWidth(width);
        this.setHeight(height);

        return this;
    },

    /**
     * Set the minimum width of this Element.
     * @param {Number/String} width The new minimum width.
     * @return {Ext.dom.Element} this
     */
    setMinWidth: function(width) {
        return this.setLengthValue(this.MIN_WIDTH, width);
    },

    /**
     * Set the minimum height of this Element.
     * @param {Number/String} height The new minimum height.
     * @return {Ext.dom.Element} this
     */
    setMinHeight: function(height) {
        return this.setLengthValue(this.MIN_HEIGHT, height);
    },

    /**
     * Set the maximum width of this Element.
     * @param {Number/String} width The new maximum width.
     * @return {Ext.dom.Element} this
     */
    setMaxWidth: function(width) {
        return this.setLengthValue(this.MAX_WIDTH, width);
    },

    /**
     * Set the maximum height of this Element.
     * @param {Number/String} height The new maximum height.
     * @return {Ext.dom.Element} this
     */
    setMaxHeight: function(height) {
        return this.setLengthValue(this.MAX_HEIGHT, height);
    },

    /**
     * Sets the element's top position directly using CSS style (instead of {@link #setY}).
     * @param {String} top The top CSS property value.
     * @return {Ext.dom.Element} this
     */
    setTop: function(top) {
        return this.setLengthValue(this.TOP, top);
    },

    /**
     * Sets the element's CSS right style.
     * @param {String} right The right CSS property value.
     * @return {Ext.dom.Element} this
     */
    setRight: function(right) {
        return this.setLengthValue(this.RIGHT, right);
    },

    /**
     * Sets the element's CSS bottom style.
     * @param {String} bottom The bottom CSS property value.
     * @return {Ext.dom.Element} this
     */
    setBottom: function(bottom) {
        return this.setLengthValue(this.BOTTOM, bottom);
    },

    /**
     * Sets the element's left position directly using CSS style (instead of {@link #setX}).
     * @param {String} left The left CSS property value.
     * @return {Ext.dom.Element} this
     */
    setLeft: function(left) {
        return this.setLengthValue(this.LEFT, left);
    },

    setMargin: function(margin) {
        var domStyle = this.dom.style;

        if (margin || margin === 0) {
            margin = this.self.unitizeBox((margin === true) ? 5 : margin);
            domStyle.setProperty('margin', margin, 'important');
        }
        else {
            domStyle.removeProperty('margin-top');
            domStyle.removeProperty('margin-right');
            domStyle.removeProperty('margin-bottom');
            domStyle.removeProperty('margin-left');
        }
    },

    setPadding: function(padding) {
        var domStyle = this.dom.style;

        if (padding || padding === 0) {
            padding = this.self.unitizeBox((padding === true) ? 5 : padding);
            domStyle.setProperty('padding', padding, 'important');
        }
        else {
            domStyle.removeProperty('padding-top');
            domStyle.removeProperty('padding-right');
            domStyle.removeProperty('padding-bottom');
            domStyle.removeProperty('padding-left');
        }
    },

    setBorder: function(border) {
        var domStyle = this.dom.style;

        if (border || border === 0) {
            border = this.self.unitizeBox((border === true) ? 1 : border);
            domStyle.setProperty('border-width', border, 'important');
        }
        else {
            domStyle.removeProperty('border-top-width');
            domStyle.removeProperty('border-right-width');
            domStyle.removeProperty('border-bottom-width');
            domStyle.removeProperty('border-left-width');
        }
    },

    setLengthValue: function(name, value) {
        var domStyle = this.dom.style;

        if (value === null) {
            domStyle.removeProperty(name);
            return this;
        }

        if (typeof value == 'number') {
            value = value + 'px';
        }

        domStyle.setProperty(name, value, 'important');
        return this;
    },

    /**
     * Sets the visibility of the element (see details). If the `visibilityMode` is set to `Element.DISPLAY`, it will use
     * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the `visibility` property.
     * @param {Boolean} visible Whether the element is visible.
     * @return {Ext.Element} this
     */
    setVisible: function(visible) {
        var mode = this.getVisibilityMode(),
            method = visible ? 'removeCls' : 'addCls';

        switch (mode) {
            case this.VISIBILITY:
                this.removeCls(['x-hidden-display', 'x-hidden-offsets']);
                this[method]('x-hidden-visibility');
                break;

            case this.DISPLAY:
                this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
                this[method]('x-hidden-display');
                break;

            case this.OFFSETS:
                this.removeCls(['x-hidden-visibility', 'x-hidden-display']);
                this[method]('x-hidden-offsets');
                break;
        }

        return this;
    },

    getVisibilityMode: function() {
        var dom = this.dom,
            mode = Ext.dom.Element.data(dom, 'visibilityMode');

        if (mode === undefined) {
            Ext.dom.Element.data(dom, 'visibilityMode', mode = this.DISPLAY);
        }

        return mode;
    },

    /**
     * Use this to change the visibility mode between {@link #VISIBILITY}, {@link #DISPLAY} or {@link #OFFSETS}.
     */
    setVisibilityMode: function(mode) {
        this.self.data(this.dom, 'visibilityMode', mode);

        return this;
    },

    /**
     * Shows this element.
     * Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
     */
    show: function() {
        var dom = this.dom;
        if (dom) {
            dom.style.removeProperty('display');
        }
    },

    /**
     * Hides this element.
     * Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
     */
    hide: function() {
        this.dom.style.setProperty('display', 'none', 'important');
    },

    setVisibility: function(isVisible) {
        var domStyle = this.dom.style;

        if (isVisible) {
            domStyle.removeProperty('visibility');
        }
        else {
            domStyle.setProperty('visibility', 'hidden', 'important');
        }
    },

    /**
     * This shared object is keyed by style name (e.g., 'margin-left' or 'marginLeft'). The
     * values are objects with the following properties:
     *
     *  * `name` (String) : The actual name to be presented to the DOM. This is typically the value
     *      returned by {@link #normalize}.
     *  * `get` (Function) : A hook function that will perform the get on this style. These
     *      functions receive "(dom, el)" arguments. The `dom` parameter is the DOM Element
     *      from which to get the style. The `el` argument (may be `null`) is the Ext.Element.
     *  * `set` (Function) : A hook function that will perform the set on this style. These
     *      functions receive "(dom, value, el)" arguments. The `dom` parameter is the DOM Element
     *      from which to get this style. The `value` parameter is the new value for the style. The
     *      `el` argument (may be `null`) is the Ext.Element.
     *
     * The `this` pointer is the object that contains `get` or `set`, which means that
     * `this.name` can be accessed if needed. The hook functions are both optional.
     * @private
     */
    styleHooks: {},

    // @private
    addStyles: function(sides, styles) {
        var totalSize = 0,
            sidesArr = sides.match(this.wordsRe),
            i = 0,
            len = sidesArr.length,
            side, size;
        for (; i < len; i++) {
            side = sidesArr[i];
            size = side && parseInt(this.getStyle(styles[side]), 10);
            if (size) {
                totalSize += Math.abs(size);
            }
        }
        return totalSize;
    },

    /**
     * Checks if the current value of a style is equal to a given value.
     * @param {String} style property whose value is returned.
     * @param {String} value to check against.
     * @return {Boolean} `true` for when the current value equals the given value.
     */
    isStyle: function(style, val) {
        return this.getStyle(style) == val;
    },

    getStyleValue: function(name) {
        return this.dom.style.getPropertyValue(name);
    },

    /**
     * Normalizes `currentStyle` and `computedStyle`.
     * @param {String} prop The style property whose value is returned.
     * @return {String} The current value of the style property for this element.
     */
    getStyle: function(prop) {
        var me = this,
            dom = me.dom,
            hook = me.styleHooks[prop],
            cs, result;

        if (dom == document) {
            return null;
        }
        if (!hook) {
            me.styleHooks[prop] = hook = { name: Ext.dom.Element.normalize(prop) };
        }
        if (hook.get) {
            return hook.get(dom, me);
        }

        cs = window.getComputedStyle(dom, '');

        // why the dom.style lookup? It is not true that "style == computedStyle" as
        // well as the fact that 0/false are valid answers...
        result = (cs && cs[hook.name]); // || dom.style[hook.name];

        // WebKit returns rgb values for transparent, how does this work n IE9+
        //        if (!supportsTransparentColor && result == 'rgba(0, 0, 0, 0)') {
        //            result = 'transparent';
        //        }

        return result;
    },

    /**
     * Wrapper for setting style properties, also takes single object parameter of multiple styles.
     * @param {String/Object} property The style property to be set, or an object of multiple styles.
     * @param {String} [value] The value to apply to the given property, or `null` if an object was passed.
     * @return {Ext.dom.Element} this
     */
    setStyle: function(prop, value) {
        var me = this,
            dom = me.dom,
            hooks = me.styleHooks,
            style = dom.style,
            valueFrom = Ext.valueFrom,
            name, hook;

        // we don't promote the 2-arg form to object-form to avoid the overhead...
        if (typeof prop == 'string') {
            hook = hooks[prop];

            if (!hook) {
                hooks[prop] = hook = { name: Ext.dom.Element.normalize(prop) };
            }
            value = valueFrom(value, '');

            if (hook.set) {
                hook.set(dom, value, me);
            } else {
                style[hook.name] = value;
            }
        }
        else {
            for (name in prop) {
                if (prop.hasOwnProperty(name)) {
                    hook = hooks[name];

                    if (!hook) {
                        hooks[name] = hook = { name: Ext.dom.Element.normalize(name) };
                    }

                    value = valueFrom(prop[name], '');

                    if (hook.set) {
                        hook.set(dom, value, me);
                    }
                    else {
                        style[hook.name] = value;
                    }
                }
            }
        }

        return me;
    },

    /**
     * Returns the offset height of the element.
     * @param {Boolean} [contentHeight] `true` to get the height minus borders and padding.
     * @return {Number} The element's height.
     */
    getHeight: function(contentHeight) {
        var dom = this.dom,
            height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
        return height > 0 ? height : 0;
    },

    /**
     * Returns the offset width of the element.
     * @param {Boolean} [contentWidth] `true` to get the width minus borders and padding.
     * @return {Number} The element's width.
     */
    getWidth: function(contentWidth) {
        var dom = this.dom,
            width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
        return width > 0 ? width : 0;
    },

    /**
     * Gets the width of the border(s) for the specified side(s)
     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
     * passing `'lr'` would get the border **l**eft width + the border **r**ight width.
     * @return {Number} The width of the sides passed added together
     */
    getBorderWidth: function(side) {
        return this.addStyles(side, this.borders);
    },

    /**
     * Gets the width of the padding(s) for the specified side(s).
     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
     * passing `'lr'` would get the padding **l**eft + the padding **r**ight.
     * @return {Number} The padding of the sides passed added together.
     */
    getPadding: function(side) {
        return this.addStyles(side, this.paddings);
    },

    /**
     * More flexible version of {@link #setStyle} for setting style properties.
     * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form `{width:"100px"}`, or
     * a function which returns such a specification.
     * @return {Ext.dom.Element} this
     */
    applyStyles: function(styles) {
        if (styles) {
            var dom = this.dom,
                styleType, i, len;

            if (typeof styles == 'function') {
                styles = styles.call();
            }
            styleType = typeof styles;
            if (styleType == 'string') {
                styles = Ext.util.Format.trim(styles).split(this.styleSplitRe);
                for (i = 0, len = styles.length; i < len;) {
                    dom.style[Ext.dom.Element.normalize(styles[i++])] = styles[i++];
                }
            }
            else if (styleType == 'object') {
                this.setStyle(styles);
            }
        }
        return this;
    },

    /**
     * Returns the size of the element.
     * @param {Boolean} [contentSize] `true` to get the width/size minus borders and padding.
     * @return {Object} An object containing the element's size:
     * @return {Number} return.width
     * @return {Number} return.height
     */
    getSize: function(contentSize) {
        var dom = this.dom;
        return {
            width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
            height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
        };
    },

    /**
     * Forces the browser to repaint this element.
     * @return {Ext.dom.Element} this
     */
    repaint: function() {
        var dom = this.dom;
        this.addCls(Ext.baseCSSPrefix + 'repaint');
        setTimeout(function() {
            Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
        }, 1);
        return this;
    },

    /**
     * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
     * then it returns the calculated width of the sides (see {@link #getPadding}).
     * @param {String} [sides] Any combination of 'l', 'r', 't', 'b' to get the sum of those sides.
     * @return {Object/Number}
     */
    getMargin: function(side) {
        var me = this,
            hash = {t: "top", l: "left", r: "right", b: "bottom"},
            o = {},
            key;

        if (!side) {
            for (key in me.margins) {
                o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
            }
            return o;
        } else {
            return me.addStyles.call(me, side, me.margins);
        }
    },

    translate: function() {
        var transformStyleName = 'webkitTransform' in document.createElement('div').style ? 'webkitTransform' : 'transform';

        return function(x, y, z) {
            this.dom.style[transformStyleName] = 'translate3d(' + (x || 0) + 'px, ' + (y || 0) + 'px, ' + (z || 0) + 'px)';
        }
    }()
});

//<deprecated product=touch since=2.0>
Ext.dom.Element.addMembers({
    /**
     * Returns the dimensions of the element available to lay content out in.
     *
     * If the element (or any ancestor element) has CSS style `display: none`, the dimensions will be zero.
     *
     * Example:
     *
     *     var vpSize = Ext.getBody().getViewSize();
     *
     *     // all Windows created afterwards will have a default value of 90% height and 95% width
     *     Ext.Window.override({
     *         width: vpSize.width * 0.9,
     *         height: vpSize.height * 0.95
     *     });
     *     // To handle window resizing you would have to hook onto onWindowResize.
     *
     * @deprecated 2.0.0
     * @return {Object} Object describing `width` and `height`:
     * @return {Number} return.width
     * @return {Number} return.height
     */
    getViewSize: function() {
        //<debug warn>
        Ext.Logger.deprecate("Ext.dom.Element.getViewSize() is deprecated", this);
        //</debug>

        var doc = document,
            dom = this.dom;

        if (dom == doc || dom == doc.body) {
            return {
                width: Element.getViewportWidth(),
                height: Element.getViewportHeight()
            };
        }
        else {
            return {
                width: dom.clientWidth,
                height: dom.clientHeight
            };
        }
    },

    /**
     * Returns `true` if the value of the given property is visually transparent. This
     * may be due to a 'transparent' style value or an rgba value with 0 in the alpha
     * component.
     * @deprecated 2.0.0
     * @param {String} prop The style property whose value is to be tested.
     * @return {Boolean} `true` if the style property is visually transparent.
     */
    isTransparent: function(prop) {
        //<debug warn>
        Ext.Logger.deprecate("Ext.dom.Element.isTransparent() is deprecated", this);
        //</debug>

        var value = this.getStyle(prop);

        return value ? this.transparentRe.test(value) : false;
    },

    /**
     * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
     * @deprecated 2.0.0
     * @param {String/String[]} className The CSS class to add, or an array of classes.
     * @return {Ext.dom.Element} this
     */
    radioCls: function(className) {
        //<debug warn>
        Ext.Logger.deprecate("Ext.dom.Element.radioCls() is deprecated", this);
        //</debug>

        var cn = this.dom.parentNode.childNodes,
            v;
        className = Ext.isArray(className) ? className : [className];
        for (var i = 0, len = cn.length; i < len; i++) {
            v = cn[i];
            if (v && v.nodeType == 1) {
                Ext.fly(v, '_internal').removeCls(className);
            }
        }
        return this.addCls(className);
    }
});
//</deprecated>


/** function getAllKeys
 *
 * @param {*} forValue The value that has multiple keys
 * @param {Object} inObject The object under inspection
 * @returns {Array} Returns keys in an array
 *
 * @example
 *
 * getAllKeysFor('faf', { 'f':'faf', 'fa': 'faf', 'faf': 'faf', 'fafa':'fafa'})
 * returns: ["f", "fa", "faf"]
 */
function getAllKeys(forValue, inObject) {
    var keys = []
    for (let key of Object.keys(inObject)) {
        if (inObject[key] === forValue) {
            keys.push(key)
        }
    }
    return keys
}

module.exports = {
    getAllKeys
}


"use strict";

var Bluebird = require("bluebird"),
	chalk = require("chalk"),
	childProcess = require("child_process"),
	exec = require("../exec"),
	fs = require("fs"),
	path = require("path"),
	preCommitBase = require("../pre-commit/pre-commit-base");

var chalkCtx = new chalk.constructor({ enabled : true });

var counter = 0,
	filesLength = 0,
	fullOutput = "",
	hasError = false,
	lineLength = 65,
	lineSpacing = (new Array(lineLength + 1)).join(" ");

/**
 * Write a character to the screen indicating lint status
 * @param {String} indicator
 */
function printCounter(indicator) {
	counter++;
	process.stdout.write(indicator);
	if (counter === filesLength || counter % lineLength === 0) {
		process.stdout.write(lineSpacing.slice(-1 * ((lineLength - counter) % lineLength)) + " ");
		process.stdout.write(String("   " + counter).slice(-3) + " / " + String("   " + filesLength).slice(-3));
		process.stdout.write("\n");
	}
}

/**
 * pre-commit hook for checking for linting errors
 * @returns {Promise}
 */
module.exports = function preCommit(/* argv */) {
	if (!preCommitBase.fileExists(".precommitrc")) {
		return Bluebird.resolve(true);
	}
	return exec("git rev-parse --verify HEAD")
		.then(function() {
			return "HEAD";
		})
		.catch(function() {
			// Initial commit: diff against an empty tree object
			return "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
		})
		.then(function(rev) {
			return exec("git diff-index --cached --full-index " + rev);
		})
		.then(function(res) {
			return res[0].replace(/\r/, "")
				.trim()
				.split(/\n/)
				.map(function(ln) {
					var parts = ln.split(/\t/);
					var gitInfo = parts[0].split(" ");
					return {
						oldMode : gitInfo[0].replace(/^:/, ""),
						newMode : gitInfo[1],
						oldSha : gitInfo[2],
						newSha : gitInfo[3],
						status : gitInfo[4],
						filename : parts[1]
					};
				});
		})
		.then(function(files) {
			filesLength = files.length;
			return files;
		})
		.each(function(file) {
			// do not check deleted files
			if (file.status === "D") {
				printCounter("D");
				return;
			}
			// do not check symlinks
			if (file.newMode === "120000") {
				printCounter("L");
				return;
			}
			// skipped files
			if (preCommitBase.isIgnored(file.filename)) {
				printCounter("S");
				return;
			}
			return exec("git cat-file -p " + file.newSha)
				.then(function(res) {
					return fs.writeFileSync(".git/pre-commit-tmpfile", res[0]);
				})
				.then(function() {
					return new Bluebird(function(resolve) {
						var child = childProcess.spawn(
							process.execPath,
							[
								path.join(__dirname, "../pre-commit", "pre-commit-check.js"),
								file.filename,
								".git/pre-commit-tmpfile"
							],
							{
								cwd : process.cwd(),
								env : process.env
							}
						);

						child.stdout.on("data", function(data) {
							fullOutput += data.toString("utf8");
						});

						child.stderr.on("data", function(data) {
							process.stderr.write(data.toString("utf8"));
						});

						child.on("close", function(code) {
							if (code !== 0) {
								hasError = true;
								printCounter(chalkCtx.red.bold("E"));
							} else {
								printCounter(".");
							}
							resolve();
						});
					});
				});
		})
		.then(function() {
			fs.unlinkSync(".git/pre-commit-tmpfile");
			if (fullOutput) {
				console.log(fullOutput);
			}
			if (hasError) {
				throw new Error("pre-commit hook failed.");
			}
		});
};


/**
 * @private
 * @author Jacky Nguyen <jacky@sencha.com>
 *
 * This class is a factory class that will create and return an animation class based on the {@link #type} configuration.
 */
Ext.define('Ext.fx.Animation', {

    requires: [
        'Ext.fx.animation.Slide',
        'Ext.fx.animation.SlideOut',
        'Ext.fx.animation.Fade',
        'Ext.fx.animation.FadeOut',
        'Ext.fx.animation.Flip',
        'Ext.fx.animation.Pop',
        'Ext.fx.animation.PopOut'
    ],

    /**
     * @cfg {String} type The type of animation to use. The possible values are:
     *
     *  - `fade` - {@link Ext.fx.animation.Fade}
     *  - `fadeOut` - {@link Ext.fx.animation.FadeOut}
     *  - `flip` - {@link Ext.fx.animation.Flip}
     *  - `pop` - {@link Ext.fx.animation.Pop}
     *  - `popOut` - {@link Ext.fx.animation.PopOut}
     *  - `slide` - {@link Ext.fx.animation.Slide}
     *  - `slideOut` - {@link Ext.fx.animation.SlideOut}
     */

    constructor: function(config) {
        var defaultClass = Ext.fx.animation.Abstract,
            type;

        if (typeof config == 'string') {
            type = config;
            config = {};
        }
        else if (config && config.type) {
            type = config.type;
        }

        if (type) {
            if (Ext.browser.is.AndroidStock2) {
                if (type == 'pop') {
                    type = 'fade';
                }
                if (type == 'popIn') {
                    type = 'fadeIn';
                }
                if (type == 'popOut') {
                    type = 'fadeOut';
                }
            }
            defaultClass = Ext.ClassManager.getByAlias('animation.' + type);

            //<debug error>
            if (!defaultClass) {
                Ext.Logger.error("Invalid animation type of: '" + type + "'");
            }
            //</debug>
        }

        return Ext.factory(config, defaultClass);
    }
});