content: /**
   * 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 Text
   * @flow
   */
  'use strict';
  
  const ColorPropType = require('ColorPropType');
  const EdgeInsetsPropType = require('EdgeInsetsPropType');
  const NativeMethodsMixin = require('NativeMethodsMixin');
  const Platform = require('Platform');
  const React = require('React');
  const PropTypes = require('prop-types');
  const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
  const StyleSheetPropType = require('StyleSheetPropType');
  const TextStylePropTypes = require('TextStylePropTypes');
  const Touchable = require('Touchable');
  
  const createReactNativeComponentClass = require('createReactNativeComponentClass');
  const mergeFast = require('mergeFast');
  const processColor = require('processColor');
  
  const stylePropType = StyleSheetPropType(TextStylePropTypes);
  
  const viewConfig = {
    validAttributes: mergeFast(ReactNativeViewAttributes.UIView, {
      isHighlighted: true,
      numberOfLines: true,
      ellipsizeMode: true,
      allowFontScaling: true,
      disabled: true,
      selectable: true,
      selectionColor: true,
      adjustsFontSizeToFit: true,
      minimumFontScale: true,
      textBreakStrategy: true,
    }),
    uiViewClassName: 'RCTText',
  };
  
  /**
   * A React component for displaying text.
   *
   * `Text` supports nesting, styling, and touch handling.
   *
   * In the following example, the nested title and body text will inherit the `fontFamily` from
   *`styles.baseText`, but the title provides its own additional styles.  The title and body will
   * stack on top of each other on account of the literal newlines:
   *
   * ```ReactNativeWebPlayer
   * import React, { Component } from 'react';
   * import { AppRegistry, Text, StyleSheet } from 'react-native';
   *
   * export default class TextInANest extends Component {
   *   constructor(props) {
   *     super(props);
   *     this.state = {
   *       titleText: "Bird's Nest",
   *       bodyText: 'This is not really a bird nest.'
   *     };
   *   }
   *
   *   render() {
   *     return (
   *       <Text style={styles.baseText}>
   *         <Text style={styles.titleText} onPress={this.onPressTitle}>
   *           {this.state.titleText}{'\n'}{'\n'}
   *         </Text>
   *         <Text numberOfLines={5}>
   *           {this.state.bodyText}
   *         </Text>
   *       </Text>
   *     );
   *   }
   * }
   *
   * const styles = StyleSheet.create({
   *   baseText: {
   *     fontFamily: 'Cochin',
   *   },
   *   titleText: {
   *     fontSize: 20,
   *     fontWeight: 'bold',
   *   },
   * });
   *
   * // skip this line if using Create React Native App
   * AppRegistry.registerComponent('TextInANest', () => TextInANest);
   * ```
   */
  
  // $FlowFixMe(>=0.41.0)
  const Text = React.createClass({
    propTypes: {
      /**
       * When `numberOfLines` is set, this prop defines how text will be truncated.
       * `numberOfLines` must be set in conjunction with this prop.
       *
       * This can be one of the following values:
       *
       * - `head` - The line is displayed so that the end fits in the container and the missing text
       * at the beginning of the line is indicated by an ellipsis glyph. e.g., "...wxyz"
       * - `middle` - The line is displayed so that the beginning and end fit in the container and the
       * missing text in the middle is indicated by an ellipsis glyph. "ab...yz"
       * - `tail` - The line is displayed so that the beginning fits in the container and the
       * missing text at the end of the line is indicated by an ellipsis glyph. e.g., "abcd..."
       * - `clip` - Lines are not drawn past the edge of the text container.
       *
       * The default is `tail`.
       *
       * > `clip` is working only for iOS
       */
      ellipsizeMode: PropTypes.oneOf(['head', 'middle', 'tail', 'clip']),
      /**
       * Used to truncate the text with an ellipsis after computing the text
       * layout, including line wrapping, such that the total number of lines
       * does not exceed this number.
       *
       * This prop is commonly used with `ellipsizeMode`.
       */
      numberOfLines: PropTypes.number,
      /**
       * Set text break strategy on Android API Level 23+, possible values are `simple`, `highQuality`, `balanced`
       * The default value is `highQuality`.
       * @platform android
       */
      textBreakStrategy: PropTypes.oneOf(['simple', 'highQuality', 'balanced']),
      /**
       * Invoked on mount and layout changes with
       *
       *   `{nativeEvent: {layout: {x, y, width, height}}}`
       */
      onLayout: PropTypes.func,
      /**
       * This function is called on press.
       *
       * e.g., `onPress={() => console.log('1st')}`
       */
      onPress: PropTypes.func,
      /**
       * This function is called on long press.
       *
       * e.g., `onLongPress={this.increaseSize}>`
       */
      onLongPress: PropTypes.func,
      /**
       * When the scroll view is disabled, this defines how far your touch may
       * move off of the button, before deactivating the button. Once deactivated,
       * try moving it back and you'll see that the button is once again
       * reactivated! Move it back and forth several times while the scroll view
       * is disabled. Ensure you pass in a constant to reduce memory allocations.
       */
      pressRetentionOffset: EdgeInsetsPropType,
      /**
       * Lets the user select text, to use the native copy and paste functionality.
       */
      selectable: PropTypes.bool,
      /**
       * The highlight color of the text.
       * @platform android
       */
      selectionColor: ColorPropType,
      /**
       * When `true`, no visual change is made when text is pressed down. By
       * default, a gray oval highlights the text on press down.
       * @platform ios
       */
      suppressHighlighting: PropTypes.bool,
      style: stylePropType,
      /**
       * Used to locate this view in end-to-end tests.
       */
      testID: PropTypes.string,
      /**
       * Used to locate this view from native code.
       * @platform android
       */
      nativeID: PropTypes.string,
      /**
       * Specifies whether fonts should scale to respect Text Size accessibility settings. The
       * default is `true`.
       */
      allowFontScaling: PropTypes.bool,
      /**
       * When set to `true`, indicates that the view is an accessibility element. The default value
       * for a `Text` element is `true`.
       *
       * See the
       * [Accessibility guide](docs/accessibility.html#accessible-ios-android)
       * for more information.
       */
      accessible: PropTypes.bool,
      /**
       * Specifies whether font should be scaled down automatically to fit given style constraints.
       * @platform ios
       */
      adjustsFontSizeToFit: PropTypes.bool,
  
      /**
       * Specifies smallest possible scale a font can reach when adjustsFontSizeToFit is enabled. (values 0.01-1.0).
       * @platform ios
       */
      minimumFontScale: PropTypes.number,
      /**
       * Specifies the disabled state of the text view for testing purposes
       * @platform android
       */
      disabled: PropTypes.bool,
    },
    getDefaultProps(): Object {
      return {
        accessible: true,
        allowFontScaling: true,
        ellipsizeMode: 'tail',
        disabled: false,
      };
    },
    getInitialState: function(): Object {
      return mergeFast(Touchable.Mixin.touchableGetInitialState(), {
        isHighlighted: false,
      });
    },
    mixins: [NativeMethodsMixin],
    viewConfig: viewConfig,
    getChildContext(): Object {
      return {isInAParentText: true};
    },
    childContextTypes: {
      isInAParentText: PropTypes.bool
    },
    contextTypes: {
      isInAParentText: PropTypes.bool
    },
    /**
     * Only assigned if touch is needed.
     */
    _handlers: (null: ?Object),
    _hasPressHandler(): boolean {
      return !!this.props.onPress || !!this.props.onLongPress;
    },
    /**
     * These are assigned lazily the first time the responder is set to make plain
     * text nodes as cheap as possible.
     */
    touchableHandleActivePressIn: (null: ?Function),
    touchableHandleActivePressOut: (null: ?Function),
    touchableHandlePress: (null: ?Function),
    touchableHandleLongPress: (null: ?Function),
    touchableGetPressRectOffset: (null: ?Function),
    render(): React.Element<any> {
      let newProps = this.props;
      if (this.props.onStartShouldSetResponder || this._hasPressHandler()) {
        if (!this._handlers) {
          this._handlers = {
            onStartShouldSetResponder: (): bool => {
              const shouldSetFromProps = this.props.onStartShouldSetResponder &&
                  // $FlowFixMe(>=0.41.0)
                  this.props.onStartShouldSetResponder();
              const setResponder = shouldSetFromProps || this._hasPressHandler();
              if (setResponder && !this.touchableHandleActivePressIn) {
                // Attach and bind all the other handlers only the first time a touch
                // actually happens.
                for (const key in Touchable.Mixin) {
                  if (typeof Touchable.Mixin[key] === 'function') {
                    (this: any)[key] = Touchable.Mixin[key].bind(this);
                  }
                }
                this.touchableHandleActivePressIn = () => {
                  if (this.props.suppressHighlighting || !this._hasPressHandler()) {
                    return;
                  }
                  this.setState({
                    isHighlighted: true,
                  });
                };
  
                this.touchableHandleActivePressOut = () => {
                  if (this.props.suppressHighlighting || !this._hasPressHandler()) {
                    return;
                  }
                  this.setState({
                    isHighlighted: false,
                  });
                };
  
                this.touchableHandlePress = (e: SyntheticEvent) => {
                  this.props.onPress && this.props.onPress(e);
                };
  
                this.touchableHandleLongPress = (e: SyntheticEvent) => {
                  this.props.onLongPress && this.props.onLongPress(e);
                };
  
                this.touchableGetPressRectOffset = function(): RectOffset {
                  return this.props.pressRetentionOffset || PRESS_RECT_OFFSET;
                };
              }
              // $FlowFixMe(>=0.41.0)
              return setResponder;
            },
            onResponderGrant: function(e: SyntheticEvent, dispatchID: string) {
              // $FlowFixMe(>=0.41.0)
              this.touchableHandleResponderGrant(e, dispatchID);
              this.props.onResponderGrant &&
                // $FlowFixMe(>=0.41.0)
                this.props.onResponderGrant.apply(this, arguments);
            }.bind(this),
            onResponderMove: function(e: SyntheticEvent) {
              // $FlowFixMe(>=0.41.0)
              this.touchableHandleResponderMove(e);
              this.props.onResponderMove &&
                // $FlowFixMe(>=0.41.0)
                this.props.onResponderMove.apply(this, arguments);
            }.bind(this),
            onResponderRelease: function(e: SyntheticEvent) {
              // $FlowFixMe(>=0.41.0)
              this.touchableHandleResponderRelease(e);
              this.props.onResponderRelease &&
                // $FlowFixMe(>=0.41.0)
                this.props.onResponderRelease.apply(this, arguments);
            }.bind(this),
            onResponderTerminate: function(e: SyntheticEvent) {
              // $FlowFixMe(>=0.41.0)
              this.touchableHandleResponderTerminate(e);
              this.props.onResponderTerminate &&
                // $FlowFixMe(>=0.41.0)
                this.props.onResponderTerminate.apply(this, arguments);
            }.bind(this),
            onResponderTerminationRequest: function(): bool {
              // Allow touchable or props.onResponderTerminationRequest to deny
              // the request
              // $FlowFixMe(>=0.41.0)
              var allowTermination = this.touchableHandleResponderTerminationRequest();
              if (allowTermination && this.props.onResponderTerminationRequest) {
                // $FlowFixMe(>=0.41.0)
                allowTermination = this.props.onResponderTerminationRequest.apply(this, arguments);
              }
              return allowTermination;
            }.bind(this),
          };
        }
        newProps = {
          ...this.props,
          ...this._handlers,
          isHighlighted: this.state.isHighlighted,
        };
      }
      if (newProps.selectionColor != null) {
        newProps = {
          ...newProps,
          selectionColor: processColor(newProps.selectionColor)
        };
      }
      if (Touchable.TOUCH_TARGET_DEBUG && newProps.onPress) {
        newProps = {
          ...newProps,
          style: [this.props.style, {color: 'magenta'}],
        };
      }
      if (this.context.isInAParentText) {
        return <RCTVirtualText {...newProps} />;
      } else {
        return <RCTText {...newProps} />;
      }
    },
  });
  
  type RectOffset = {
    top: number,
    left: number,
    right: number,
    bottom: number,
  }
  
  var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
  
  var RCTText = createReactNativeComponentClass(viewConfig);
  var RCTVirtualText = RCTText;
  
  if (Platform.OS === 'android') {
    RCTVirtualText = createReactNativeComponentClass({
      validAttributes: mergeFast(ReactNativeViewAttributes.UIView, {
        isHighlighted: true,
      }),
      uiViewClassName: 'RCTVirtualText',
    });
  }
  
  module.exports = Text;
  