text: /**
   * Copyright (c) 2015-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   *
   * @providesModule NativeMethodsMixin
   * @flow
   */
  'use strict';
  
  var NativeModules = require('NativeModules');
  var RCTPOPAnimationManager = NativeModules.POPAnimationManager;
  var RCTUIManager = NativeModules.UIManager;
  var TextInputState = require('TextInputState');
  
  var findNodeHandle = require('findNodeHandle');
  var flattenStyle = require('flattenStyle');
  var invariant = require('invariant');
  var mergeFast = require('mergeFast');
  var precomputeStyle = require('precomputeStyle');
  
  type MeasureOnSuccessCallback = (
    x: number,
    y: number,
    width: number,
    height: number,
    pageX: number,
    pageY: number
  ) => void
  
  type MeasureLayoutOnSuccessCallback = (
    left: number,
    top: number,
    width: number,
    height: number
  ) => void
  
  var animationIDInvariant = function(
    funcName: string,
    anim: number
  ) {
    invariant(
      anim,
      funcName + ' must be called with a valid animation ID returned from' +
      ' POPAnimation.createAnimation, received: "' + anim + '"'
    );
  };
  
  var NativeMethodsMixin = {
    addAnimation: function(anim: number, callback?: (finished: bool) => void) {
      animationIDInvariant('addAnimation', anim);
      RCTPOPAnimationManager.addAnimation(
        findNodeHandle(this),
        anim,
        mountSafeCallback(this, callback)
      );
    },
  
    removeAnimation: function(anim: number) {
      animationIDInvariant('removeAnimation', anim);
      RCTPOPAnimationManager.removeAnimation(findNodeHandle(this), anim);
    },
  
    measure: function(callback: MeasureOnSuccessCallback) {
      RCTUIManager.measure(
        findNodeHandle(this),
        mountSafeCallback(this, callback)
      );
    },
  
    measureLayout: function(
      relativeToNativeNode: number,
      onSuccess: MeasureLayoutOnSuccessCallback,
      onFail: () => void /* currently unused */
    ) {
      RCTUIManager.measureLayout(
        findNodeHandle(this),
        relativeToNativeNode,
        mountSafeCallback(this, onFail),
        mountSafeCallback(this, onSuccess)
      );
    },
  
    /**
     * This function sends props straight to native. They will not participate
     * in future diff process, this means that if you do not include them in the
     * next render, they will remain active.
     */
    setNativeProps: function(nativeProps: Object) {
      // nativeProps contains a style attribute that's going to be flattened
      // and all the attributes expanded in place. In order to make this
      // process do as few allocations and copies as possible, we return
      // one if the other is empty. Only if both have values then we create
      // a new object and merge.
      var hasOnlyStyle = true;
      for (var key in nativeProps) {
        if (key !== 'style') {
          hasOnlyStyle = false;
          break;
        }
      }
      var style = precomputeStyle(flattenStyle(nativeProps.style));
  
      var props = null;
      if (hasOnlyStyle) {
        props = style;
      } else if (!style) {
        props = nativeProps;
      } else {
        props = mergeFast(nativeProps, style);
      }
  
      RCTUIManager.updateView(
        findNodeHandle(this),
        this.viewConfig.uiViewClassName,
        props
      );
    },
  
    focus: function() {
      TextInputState.focusTextInput(findNodeHandle(this));
    },
  
    blur: function() {
      TextInputState.blurTextInput(findNodeHandle(this));
    }
  };
  
  function throwOnStylesProp(component, props) {
    if (props.styles !== undefined) {
      var owner = component._owner || null;
      var name = component.constructor.displayName;
      var msg = '`styles` is not a supported property of `' + name + '`, did ' +
        'you mean `style` (singular)?';
      if (owner && owner.constructor && owner.constructor.displayName) {
        msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' +
          ' component.';
      }
      throw new Error(msg);
    }
  }
  if (__DEV__) {
    // hide this from Flow since we can't define these properties outside of
    // __DEV__ without actually implementing them (setting them to undefined
    // isn't allowed by ReactClass)
    var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
    invariant(
      !NativeMethodsMixin_DEV.componentWillMount &&
      !NativeMethodsMixin_DEV.componentWillReceiveProps,
      'Do not override existing functions.'
    );
    NativeMethodsMixin_DEV.componentWillMount = function () {
      throwOnStylesProp(this, this.props);
    };
    NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) {
      throwOnStylesProp(this, newProps);
    };
  }
  
  /**
   * In the future, we should cleanup callbacks by cancelling them instead of
   * using this.
   */
  var mountSafeCallback = function(context: ReactComponent, callback: ?Function): any {
    return function() {
      if (!callback || (context.isMounted && !context.isMounted())) {
        return;
      }
      return callback.apply(context, arguments);
    };
  };
  
  module.exports = NativeMethodsMixin;
  