text: /**
   * Copyright (c) 2017-present, Wonday (@wonday.org)
   * All rights reserved.
   *
   * This source code is licensed under the MIT-style license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  'use strict';
  import React, {Component} from 'react';
  import PropTypes from 'prop-types';
  import {
      requireNativeComponent,
      View,
      Platform,
      ViewPropTypes,
      StyleSheet,
      Image
  } from 'react-native';
  
  import { ProgressBar } from '@react-native-community/progress-bar-android'
  import { ProgressView } from '@react-native-community/progress-view'
  
  import RNFetchBlob from 'rn-fetch-blob';
  
  const SHA1 = require('crypto-js/sha1');
  import PdfView from './PdfView';
  
  export default class Pdf extends Component {
  
      static propTypes = {
          ...ViewPropTypes,
          source: PropTypes.oneOfType([
              PropTypes.shape({
                  uri: PropTypes.string,
                  cache: PropTypes.bool,
                  expiration: PropTypes.number,
              }),
              // Opaque type returned by require('./test.pdf')
              PropTypes.number,
          ]).isRequired,
          page: PropTypes.number,
          scale: PropTypes.number,
          minScale: PropTypes.number,
          maxScale: PropTypes.number,
          horizontal: PropTypes.bool,
          spacing: PropTypes.number,
          password: PropTypes.string,
          progressBarColor: PropTypes.string,
          activityIndicator: PropTypes.any,
          activityIndicatorProps: PropTypes.any,
          enableAntialiasing: PropTypes.bool,
          enableAnnotationRendering: PropTypes.bool,
          enablePaging: PropTypes.bool,
          enableRTL: PropTypes.bool,
          fitPolicy: PropTypes.number,
          trustAllCerts: PropTypes.bool,
          singlePage: PropTypes.bool,
          onLoadComplete: PropTypes.func,
          onPageChanged: PropTypes.func,
          onError: PropTypes.func,
          onPageSingleTap: PropTypes.func,
          onScaleChanged: PropTypes.func,
          onPressLink: PropTypes.func,
  
          // Props that are not available in the earlier react native version, added to prevent crashed on android
          accessibilityLabel: PropTypes.string,
          importantForAccessibility: PropTypes.string,
          renderToHardwareTextureAndroid: PropTypes.string,
          testID: PropTypes.string,
          onLayout: PropTypes.bool,
          accessibilityLiveRegion: PropTypes.string,
          accessibilityComponentType: PropTypes.string,
      };
  
      static defaultProps = {
          password: "",
          scale: 1,
          minScale: 1,
          maxScale: 3,
          spacing: 10,
          fitPolicy: 2, //fit both
          horizontal: false,
          page: 1,
          enableAntialiasing: true,
          enableAnnotationRendering: true,
          enablePaging: false,
          enableRTL: false,
          activityIndicatorProps: {color: '#009900', progressTintColor: '#009900'},
          trustAllCerts: true,
          usePDFKit: true,
          singlePage: false,
          onLoadProgress: (percent) => {
          },
          onLoadComplete: (numberOfPages, path) => {
          },
          onPageChanged: (page, numberOfPages) => {
          },
          onError: (error) => {
          },
          onPageSingleTap: (page, x, y) => {
          },
          onScaleChanged: (scale) => {
          },
          onPressLink: (url) => {
          },
      };
  
      constructor(props) {
  
          super(props);
          this.state = {
              path: '',
              isDownloaded: false,
              progress: 0,
              isSupportPDFKit: -1
          };
  
          this.lastRNBFTask = null;
  
      }
  
      componentDidUpdate(prevProps) {
  
          const nextSource = Image.resolveAssetSource(this.props.source);
          const curSource = Image.resolveAssetSource(prevProps.source);
  
          if ((nextSource.uri !== curSource.uri)) {
              // if has download task, then cancel it.
              if (this.lastRNBFTask) {
                  this.lastRNBFTask.cancel(err => {
                      this._loadFromSource(this.props.source);
                  });
                  this.lastRNBFTask = null;
              } else {
                  this._loadFromSource(this.props.source);
              }
          }
      }
  
      componentDidMount() {
          this._mounted = true;
          if (Platform.OS === "ios") {
              const PdfViewManagerNative = require('react-native').NativeModules.PdfViewManager;
              PdfViewManagerNative.supportPDFKit((isSupportPDFKit) => {
                  if (this._mounted) {
                      this.setState({isSupportPDFKit: isSupportPDFKit ? 1 : 0});
                  }
              });
          }
          this._loadFromSource(this.props.source);
      }
  
      componentWillUnmount() {
          this._mounted = false;
          if (this.lastRNBFTask) {
              this.lastRNBFTask.cancel(err => {
              });
              this.lastRNBFTask = null;
          }
  
      }
  
      _loadFromSource = (newSource) => {
  
          const source = Image.resolveAssetSource(newSource) || {};
  
          let uri = source.uri || '';
  
          // first set to initial state
          if (this._mounted) {
              this.setState({isDownloaded: false, path: '', progress: 0});
          }
  
          const cacheFile = RNFetchBlob.fs.dirs.CacheDir + '/' + SHA1(uri) + '.pdf';
  
          if (source.cache) {
              RNFetchBlob.fs
                  .stat(cacheFile)
                  .then(stats => {
                      if (!Boolean(source.expiration) || (source.expiration * 1000 + stats.lastModified) > (new Date().getTime())) {
                          if (this._mounted) {
                              this.setState({path: cacheFile, isDownloaded: true});
                          }
                      } else {
                          // cache expirated then reload it
                          this._prepareFile(source);
                      }
                  })
                  .catch(() => {
                      this._prepareFile(source);
                  })
  
          } else {
              this._prepareFile(source);
          }
      };
  
      _prepareFile = async (source) => {
  
          try {
              if (source.uri) {
                  let uri = source.uri || '';
  
                  const isNetwork = !!(uri && uri.match(/^https?:\/\//));
                  const isAsset = !!(uri && uri.match(/^bundle-assets:\/\//));
                  const isBase64 = !!(uri && uri.match(/^data:application\/pdf;base64/));
  
                  const cacheFile = RNFetchBlob.fs.dirs.CacheDir + '/' + SHA1(uri) + '.pdf';
  
                  // delete old cache file
                  this._unlinkFile(cacheFile);
  
                  if (isNetwork) {
                      this._downloadFile(source, cacheFile);
                  } else if (isAsset) {
                      RNFetchBlob.fs
                          .cp(uri, cacheFile)
                          .then(() => {
                              if (this._mounted) {
                                  this.setState({path: cacheFile, isDownloaded: true, progress: 1});
                              }
                          })
                          .catch(async (error) => {
                              this._unlinkFile(cacheFile);
                              this._onError(error);
                          })
                  } else if (isBase64) {
                      let data = uri.replace(/data:application\/pdf;base64,/i, '');
                      RNFetchBlob.fs
                          .writeFile(cacheFile, data, 'base64')
                          .then(() => {
                              if (this._mounted) {
                                  this.setState({path: cacheFile, isDownloaded: true, progress: 1});
                              }
                          })
                          .catch(async (error) => {
                              this._unlinkFile(cacheFile);
                              this._onError(error)
                          });
                  } else {
                      if (this._mounted) {
                         this.setState({
                              path: uri.replace(/file:\/\//i, ''),
                              isDownloaded: true,
                          });
                      }
                  }
              } else {
                  this._onError(new Error('no pdf source!'));
              }
          } catch (e) {
              this._onError(e)
          }
  
  
      };
  
      _downloadFile = async (source, cacheFile) => {
  
          if (this.lastRNBFTask) {
              this.lastRNBFTask.cancel(err => {
              });
              this.lastRNBFTask = null;
          }
  
          const tempCacheFile = cacheFile + '.tmp';
          this._unlinkFile(tempCacheFile);
  
          this.lastRNBFTask = RNFetchBlob.config({
              // response data will be saved to this path if it has access right.
              path: tempCacheFile,
              trusty: this.props.trustAllCerts,
          })
              .fetch(
                  source.method ? source.method : 'GET',
                  source.uri,
                  source.headers ? source.headers : {},
                  source.body ? source.body : ""
              )
              // listen to download progress event
              .progress((received, total) => {
                  this.props.onLoadProgress && this.props.onLoadProgress(received / total);
                  if (this._mounted) {
                      this.setState({progress: received / total});
                  }
              });
  
          this.lastRNBFTask
              .then(async (res) => {
  
                  this.lastRNBFTask = null;
  
                  if (res && res.respInfo && res.respInfo.headers && !res.respInfo.headers["Content-Encoding"] && !res.respInfo.headers["Transfer-Encoding"] && res.respInfo.headers["Content-Length"]) {
                      const expectedContentLength = res.respInfo.headers["Content-Length"];
                      let actualContentLength;
  
                      try {
                          const fileStats = await RNFetchBlob.fs.stat(res.path());
  
                          if (!fileStats || !fileStats.size) {
                              throw new Error("FileNotFound:" + source.uri);
                          }
  
                          actualContentLength = fileStats.size;
                      } catch (error) {
                          throw new Error("DownloadFailed:" + source.uri);
                      }
  
                      if (expectedContentLength != actualContentLength) {
                          throw new Error("DownloadFailed:" + source.uri);
                      }
                  }
  
                  this._unlinkFile(cacheFile);
                  RNFetchBlob.fs
                      .cp(tempCacheFile, cacheFile)
                      .then(() => {
                          if (this._mounted) {
                              this.setState({path: cacheFile, isDownloaded: true, progress: 1});
                          }
                          this._unlinkFile(tempCacheFile);
                      })
                      .catch(async (error) => {
                          throw error;
                      });
              })
              .catch(async (error) => {
                  this._unlinkFile(tempCacheFile);
                  this._unlinkFile(cacheFile);
                  this._onError(error);
              });
  
      };
  
      _unlinkFile = async (file) => {
          try {
              await RNFetchBlob.fs.unlink(file);
          } catch (e) {
  
          }
      }
  
      setNativeProps = nativeProps => {
          if (this._root){
              this._root.setNativeProps(nativeProps);
          }
      };
  
      setPage( pageNumber ) {
          if ( (pageNumber === null) || (isNaN(pageNumber)) ) {
              throw new Error('Specified pageNumber is not a number');
          }
          this.setNativeProps({
              page: pageNumber
          });
      }
  
      _onChange = (event) => {
  
          let message = event.nativeEvent.message.split('|');
          //__DEV__ && console.log("onChange: " + message);
          if (message.length > 0) {
              if (message.length > 5) {
                  message[4] = message.splice(4).join('|');
              }
              if (message[0] === 'loadComplete') {
                  this.props.onLoadComplete && this.props.onLoadComplete(Number(message[1]), this.state.path, {
                      width: Number(message[2]),
                      height: Number(message[3]),
                  },
                  message[4]&&JSON.parse(message[4]));
              } else if (message[0] === 'pageChanged') {
                  this.props.onPageChanged && this.props.onPageChanged(Number(message[1]), Number(message[2]));
              } else if (message[0] === 'error') {
                  this._onError(new Error(message[1]));
              } else if (message[0] === 'pageSingleTap') {
                  this.props.onPageSingleTap && this.props.onPageSingleTap(message[1], message[2], message[3]);
              } else if (message[0] === 'scaleChanged') {
                  this.props.onScaleChanged && this.props.onScaleChanged(message[1]);
              } else if (message[0] === 'linkPressed') {
                  this.props.onPressLink && this.props.onPressLink(message[1]);
              }
          }
  
      };
  
      _onError = (error) => {
  
          this.props.onError && this.props.onError(error);
  
      };
  
      render() {
          if (Platform.OS === "android" || Platform.OS === "ios") {
                  return (
                      <View style={[this.props.style,{overflow: 'hidden'}]}>
                          {!this.state.isDownloaded?
                              (<View
                                  style={styles.progressContainer}
                              >
                                  {this.props.activityIndicator
                                      ? this.props.activityIndicator
                                      : Platform.OS === 'android'
                                          ? <ProgressBar
                                              progress={this.state.progress}
                                              indeterminate={false}
                                              styleAttr="Horizontal"
                                              style={styles.progressBar}
                                              {...this.props.activityIndicatorProps}
                                          />
                                          : <ProgressView
                                              progress={this.state.progress}
                                              style={styles.progressBar}
                                              {...this.props.activityIndicatorProps}
                                          />}
                              </View>):(
                                  Platform.OS === "android"?(
                                          <PdfCustom
                                              ref={component => (this._root = component)}
                                              {...this.props}
                                              style={[{flex:1,backgroundColor: '#EEE'}, this.props.style]}
                                              path={this.state.path}
                                              onChange={this._onChange}
                                          />
                                      ):(
                                          this.props.usePDFKit && this.state.isSupportPDFKit === 1?(
                                                  <PdfCustom
                                                      ref={component => (this._root = component)}
                                                      {...this.props}
                                                      style={[{backgroundColor: '#EEE',overflow: 'hidden'}, this.props.style]}
                                                      path={this.state.path}
                                                      onChange={this._onChange}
                                                  />
                                              ):(<PdfView
                                                  {...this.props}
                                                  style={[{backgroundColor: '#EEE',overflow: 'hidden'}, this.props.style]}
                                                  path={this.state.path}
                                                  onLoadComplete={this.props.onLoadComplete}
                                                  onPageChanged={this.props.onPageChanged}
                                                  onError={this._onError}
                                                  onPageSingleTap={this.props.onPageSingleTap}
                                                  onScaleChanged={this.props.onScaleChanged}
                                                  onPressLink={this.props.onPressLink}
                                              />)
                                      )
                                  )}
                      </View>);
          } else {
              return (null);
          }
  
  
      }
  }
  
  
  if (Platform.OS === "android") {
      var PdfCustom = requireNativeComponent('RCTPdf', Pdf, {
          nativeOnly: {path: true, onChange: true},
      })
  } else if (Platform.OS === "ios") {
      var PdfCustom = requireNativeComponent('RCTPdfView', Pdf, {
          nativeOnly: {path: true, onChange: true},
      })
  }
  
  
  const styles = StyleSheet.create({
      progressContainer: {
          flex: 1,
          justifyContent: 'center',
          alignItems: 'center'
      },
      progressBar: {
          width: 200,
          height: 2
      }
  });
  