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 createReactClass = require('create-react-class');
  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);
   * ```
   *
   * ## Nested text
   *
   * Both iOS and Android allow you to display formatted text by annotating
   * ranges of a string with specific formatting like bold or colored text
   * (`NSAttributedString` on iOS, `SpannableString` on Android). In practice,
   * this is very tedious. For React Native, we decided to use web paradigm for
   * this where you can nest text to achieve the same effect.
   *
   *
   * ```ReactNativeWebPlayer
   * import React, { Component } from 'react';
   * import { AppRegistry, Text } from 'react-native';
   *
   * export default class BoldAndBeautiful extends Component {
   *   render() {
   *     return (
   *       <Text style={{fontWeight: 'bold'}}>
   *         I am bold
   *         <Text style={{color: 'red'}}>
   *           and red
   *         </Text>
   *       </Text>
   *     );
   *   }
   * }
   *
   * // skip this line if using Create React Native App
   * AppRegistry.registerComponent('AwesomeProject', () => BoldAndBeautiful);
   * ```
   *
   * Behind the scenes, React Native converts this to a flat `NSAttributedString`
   * or `SpannableString` that contains the following information:
   *
   * ```javascript
   * "I am bold and red"
   * 0-9: bold
   * 9-17: bold, red
   * ```
   *
   * ## Nested views (iOS only)
   *
   * On iOS, you can nest views within your Text component. Here's an example:
   *
   * ```ReactNativeWebPlayer
   * import React, { Component } from 'react';
   * import { AppRegistry, Text, View } from 'react-native';
   *
   * export default class BlueIsCool extends Component {
   *   render() {
   *     return (
   *       <Text>
   *         There is a blue square
   *         <View style={{width: 50, height: 50, backgroundColor: 'steelblue'}} />
   *         in between my text.
   *       </Text>
   *     );
   *   }
   * }
   *
   * // skip this line if using Create React Native App
   * AppRegistry.registerComponent('AwesomeProject', () => BlueIsCool);
   * ```
   *
   * > In order to use this feature, you must give the view a `width` and a `height`.
   *
   * ## Containers
   *
   * The `<Text>` element is special relative to layout: everything inside is no
   * longer using the flexbox layout but using text layout. This means that
   * elements inside of a `<Text>` are no longer rectangles, but wrap when they
   * see the end of the line.
   *
   * ```javascript
   * <Text>
   *   <Text>First part and </Text>
   *   <Text>second part</Text>
   * </Text>
   * // Text container: all the text flows as if it was one
   * // |First part |
   * // |and second |
   * // |part       |
   *
   * <View>
   *   <Text>First part and </Text>
   *   <Text>second part</Text>
   * </View>
   * // View container: each text is its own block
   * // |First part |
   * // |and        |
   * // |second part|
   * ```
   *
   * ## Limited Style Inheritance
   *
   * On the web, the usual way to set a font family and size for the entire
   * document is to take advantage of inherited CSS properties like so:
   *
   * ```css
   * html {
   *   font-family: 'lucida grande', tahoma, verdana, arial, sans-serif;
   *   font-size: 11px;
   *   color: #141823;
   * }
   * ```
   *
   * All elements in the document will inherit this font unless they or one of
   * their parents specifies a new rule.
   *
   * In React Native, we are more strict about it: **you must wrap all the text
   * nodes inside of a `<Text>` component**. You cannot have a text node directly
   * under a `<View>`.
   *
   *
   * ```javascript
   * // BAD: will raise exception, can't have a text node as child of a <View>
   * <View>
   *   Some text
   * </View>
   *
   * // GOOD
   * <View>
   *   <Text>
   *     Some text
   *   </Text>
   * </View>
   * ```
   *
   * You also lose the ability to set up a default font for an entire subtree.
   * The recommended way to use consistent fonts and sizes across your
   * application is to create a component `MyAppText` that includes them and use
   * this component across your app. You can also use this component to make more
   * specific components like `MyAppHeaderText` for other kinds of text.
   *
   * ```javascript
   * <View>
   *   <MyAppText>Text styled with the default font for the entire application</MyAppText>
   *   <MyAppHeaderText>Text styled as a header</MyAppHeaderText>
   * </View>
   * ```
   *
   * Assuming that `MyAppText` is a component that simply renders out its
   * children into a `Text` component with styling, then `MyAppHeaderText` can be
   * defined as follows:
   *
   * ```javascript
   * class MyAppHeaderText extends Component {
   *   render() {
   *     return (
   *       <MyAppText>
   *         <Text style={{fontSize: 20}}>
   *           {this.props.children}
   *         </Text>
   *       </MyAppText>
   *     );
   *   }
   * }
   * ```
   *
   * Composing `MyAppText` in this way ensures that we get the styles from a
   * top-level component, but leaves us the ability to add / override them in
   * specific use cases.
   *
   * React Native still has the concept of style inheritance, but limited to text
   * subtrees. In this case, the second part will be both bold and red.
   *
   * ```javascript
   * <Text style={{fontWeight: 'bold'}}>
   *   I am bold
   *   <Text style={{color: 'red'}}>
   *     and red
   *   </Text>
   * </Text>
   * ```
   *
   * We believe that this more constrained way to style text will yield better
   * apps:
   *
   * - (Developer) React components are designed with strong isolation in mind:
   * You should be able to drop a component anywhere in your application,
   * trusting that as long as the props are the same, it will look and behave the
   * same way. Text properties that could inherit from outside of the props would
   * break this isolation.
   *
   * - (Implementor) The implementation of React Native is also simplified. We do
   * not need to have a `fontFamily` field on every single element, and we do not
   * need to potentially traverse the tree up to the root every time we display a
   * text node. The style inheritance is only encoded inside of the native Text
   * component and doesn't leak to other components or the system itself.
   *
   */
  
  const Text = createReactClass({
    displayName: 'Text',
    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.
       */
      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',
      };
    },
    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;
                };
              }
              return setResponder;
            },
            onResponderGrant: function(e: SyntheticEvent<>, dispatchID: string) {
              this.touchableHandleResponderGrant(e, dispatchID);
              this.props.onResponderGrant &&
                this.props.onResponderGrant.apply(this, arguments);
            }.bind(this),
            onResponderMove: function(e: SyntheticEvent<>) {
              this.touchableHandleResponderMove(e);
              this.props.onResponderMove &&
                this.props.onResponderMove.apply(this, arguments);
            }.bind(this),
            onResponderRelease: function(e: SyntheticEvent<>) {
              this.touchableHandleResponderRelease(e);
              this.props.onResponderRelease &&
                this.props.onResponderRelease.apply(this, arguments);
            }.bind(this),
            onResponderTerminate: function(e: SyntheticEvent<>) {
              this.touchableHandleResponderTerminate(e);
              this.props.onResponderTerminate &&
                this.props.onResponderTerminate.apply(this, arguments);
            }.bind(this),
            onResponderTerminationRequest: function(): bool {
              // Allow touchable or props.onResponderTerminationRequest to deny
              // the request
              var allowTermination = this.touchableHandleResponderTerminationRequest();
              if (allowTermination && this.props.onResponderTerminationRequest) {
                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.uiViewClassName,
    () => viewConfig
  );
  var RCTVirtualText = RCTText;
  
  if (Platform.OS === 'android') {
    RCTVirtualText = createReactNativeComponentClass('RCTVirtualText', () => ({
      validAttributes: mergeFast(ReactNativeViewAttributes.UIView, {
        isHighlighted: true,
      }),
      uiViewClassName: 'RCTVirtualText',
    }));
  }
  
  module.exports = Text;
  