├── .gitignore ├── LICENSE ├── Markdown.js ├── README.md ├── example ├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── __tests__ │ ├── index.android.js │ └── index.ios.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── index.android.js ├── index.ios.js ├── ios │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── example.xcscheme │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── package.json └── yarn.lock ├── lib ├── index.js ├── md.js └── react_helpers.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # IDE 37 | .idea 38 | 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Pierre Monge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Markdown.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { 3 | View, 4 | } from 'react-native'; 5 | import { markdown } from './lib/index'; 6 | 7 | type Props = { 8 | style: any; 9 | mdStyle: any, 10 | childre?: any; 11 | }; 12 | 13 | export default class Markdown extends Component { 14 | props: Props; 15 | 16 | static propTypes = { 17 | style: View.propTypes.style, 18 | mdStyle: PropTypes.any, 19 | }; 20 | 21 | componentDidMount() { 22 | if (this.props.mdStyle) { 23 | mdStyle = _.merge({}, this.props.mdStyle, mdStyle); 24 | } 25 | } 26 | 27 | render() { 28 | let text; 29 | if (typeof this.props.children === 'string') { 30 | text = this.props.children; 31 | } else if (typeof this.props.children === 'object') { 32 | text = this.props.children.join(''); 33 | } else { 34 | throw 'Not supported type of text: ' + typeof this.props.children; 35 | } 36 | return ( 37 | 38 | {markdown.toReact(text)} 39 | 40 | ) 41 | } 42 | } 43 | 44 | // Here is the default style for markdown 45 | let mdStyle = { 46 | h: { 47 | fontWeight: '200', 48 | }, 49 | h1: { 50 | fontSize: 32, 51 | }, 52 | h2: { 53 | fontSize: 24, 54 | }, 55 | h3: { 56 | fontSize: 18, 57 | }, 58 | h4: { 59 | fontSize: 16, 60 | }, 61 | h5: { 62 | fontSize: 13, 63 | }, 64 | h6: { 65 | fontSize: 11, 66 | }, 67 | code: { 68 | backgroundColor: '#f7f7f7', 69 | }, 70 | boxPre: { 71 | backgroundColor: '#f7f7f7', 72 | }, 73 | pre: { 74 | }, 75 | a: { 76 | color: 'blue', 77 | }, 78 | blockquote: { 79 | fontFamily: 'Courier', 80 | fontWeight: '500', 81 | color: 'grey', 82 | }, 83 | blockQuotePipe: { 84 | height: 25, 85 | width: 3, 86 | marginHorizontal: 10, 87 | backgroundColor: '#dddddd', 88 | }, 89 | del: { 90 | containerBackgroundColor: '#222222', 91 | }, 92 | em: { 93 | fontStyle: 'italic', 94 | }, 95 | strong: { 96 | fontWeight: 'bold', 97 | }, 98 | li: { 99 | 100 | }, 101 | liPoint: { 102 | height: 5, 103 | width: 5, 104 | borderRadius: 50, 105 | margin: 5, 106 | backgroundColor: 'black', 107 | }, 108 | liOrder: { 109 | fontWeight: 'bold', 110 | paddingLeft: 5, 111 | }, 112 | p: { 113 | marginTop: 10, 114 | marginBottom: 10, 115 | flexWrap: 'wrap', 116 | flexDirection: 'row', 117 | alignItems: 'flex-start', 118 | justifyContent: 'flex-start', 119 | }, 120 | }; 121 | export { mdStyle }; 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-markdown-syntax 2 | *** 3 | This component is based on the parser [markdown-js](https://github.com/evilstreak/markdown-js). 4 | 5 | ### Installation 6 | 7 | `npm install react-native-markdown-syntax` 8 | 9 | ### Usage 10 | 11 | ``` 12 | import React, { Component } from 'react'; 13 | import { 14 | AppRegistry, 15 | } from 'react-native'; 16 | import Markdown from 'react-native-markdown-syntax'; 17 | 18 | export default class example extends Component { 19 | render() { 20 | return ( 21 | 22 | {'\n\n'} 23 | Emphasis, aka italics, with *asterisks* or _underscores_.{'\n'} 24 | Strong emphasis, aka bold, with **asterisks** or __underscores__.{'\n'} 25 | Combined emphasis with **asterisks and _underscores_**.{'\n'} 26 | Inline code uses one `. `Scratch this.`{'\n'} 27 | 28 | Here comes codeblock{'\n'} 29 | > Hello 30 | __em__ 31 | `inline Code` az{'\n'}{'\n'} 32 | - ul{'\n\n​\n\n'} 33 | 1. ol1{'\n'} 34 | 2. ol2{'\n'} 35 | 3. ol3{'\n\n\nHere a new list\n\n'} 36 | - ulnew{'\n'} 37 | - [ ] checkbox not supported yet :/{'\n'} 38 | 39 | ); 40 | } 41 | } 42 | AppRegistry.registerComponent('example', () => example); 43 | ``` 44 | 45 | Based on a html parser, `react-native-markdown-syntax` support many markdown syntax, but not fully feature is implemented in react-native, here is the supported tag from web to react-native: 46 | 47 | | HTML Tag | Desc | Markdown syntax example | 48 | |------------ |---------------- |------------------------- | 49 | | h {1-6} | Header | # Hello | 50 | | strong | Bold | **Hello** | 51 | | em | Italics | _Hello_ | 52 | | p | Paragraph | Hello | 53 | | code | Code | `Hello` | 54 | | pre | Inline Code | `Hello` | 55 | | blockquote | Quote | > Hello | 56 | | ul | Unordered List | - Hello {'\n'} - Hello | 57 | | ol | Ordered List | 1. Hello {'\n'} - Hello | 58 | | li | List Item | | 59 | | br | Line Break | \n | 60 | 61 | You can pass style for each tag as mdStyle props: 62 | 63 | ``` 64 | /website/.* 14 | 15 | # Ignore BUCK generated dirs 16 | /\.buckd/ 17 | 18 | # Ignore unexpected extra @providesModule 19 | .*/node_modules/commoner/test/source/widget/share.js 20 | 21 | # Ignore duplicate module providers 22 | # For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root 23 | .*/Libraries/react-native/React.js 24 | .*/Libraries/react-native/ReactNative.js 25 | .*/node_modules/jest-runtime/build/__tests__/.* 26 | 27 | [include] 28 | 29 | [libs] 30 | node_modules/react-native/Libraries/react-native/react-native-interface.js 31 | node_modules/react-native/flow 32 | flow/ 33 | 34 | [options] 35 | module.system=haste 36 | 37 | esproposal.class_static_fields=enable 38 | esproposal.class_instance_fields=enable 39 | 40 | experimental.strict_type_args=true 41 | 42 | munge_underscores=true 43 | 44 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 45 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 46 | 47 | suppress_type=$FlowIssue 48 | suppress_type=$FlowFixMe 49 | suppress_type=$FixMe 50 | 51 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 52 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-3]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 53 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 54 | 55 | unsafe.enable_getters_and_setters=true 56 | 57 | [version] 58 | ^0.33.0 59 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | android/keystores/debug.keystore 42 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /example/__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # To learn about Buck see [Docs](https://buckbuild.com/). 4 | # To run your application with Buck: 5 | # - install Buck 6 | # - `npm start` - to start the packager 7 | # - `cd android` 8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 10 | # - `buck install -r android/app` - compile, install and run application 11 | # 12 | 13 | lib_deps = [] 14 | for jarfile in glob(['libs/*.jar']): 15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) 16 | lib_deps.append(':' + name) 17 | prebuilt_jar( 18 | name = name, 19 | binary_jar = jarfile, 20 | ) 21 | 22 | for aarfile in glob(['libs/*.aar']): 23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) 24 | lib_deps.append(':' + name) 25 | android_prebuilt_aar( 26 | name = name, 27 | aar = aarfile, 28 | ) 29 | 30 | android_library( 31 | name = 'all-libs', 32 | exported_deps = lib_deps 33 | ) 34 | 35 | android_library( 36 | name = 'app-code', 37 | srcs = glob([ 38 | 'src/main/java/**/*.java', 39 | ]), 40 | deps = [ 41 | ':all-libs', 42 | ':build_config', 43 | ':res', 44 | ], 45 | ) 46 | 47 | android_build_config( 48 | name = 'build_config', 49 | package = 'com.example', 50 | ) 51 | 52 | android_resource( 53 | name = 'res', 54 | res = 'src/main/res', 55 | package = 'com.example', 56 | ) 57 | 58 | android_binary( 59 | name = 'app', 60 | package_type = 'debug', 61 | manifest = 'src/main/AndroidManifest.xml', 62 | keystore = '//android/keystores:debug', 63 | deps = [ 64 | ':app-code', 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // the root of your project, i.e. where "package.json" lives 37 | * root: "../../", 38 | * 39 | * // where to put the JS bundle asset in debug mode 40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 41 | * 42 | * // where to put the JS bundle asset in release mode 43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 44 | * 45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 46 | * // require('./image.png')), in debug mode 47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 48 | * 49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 50 | * // require('./image.png')), in release mode 51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 52 | * 53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 57 | * // for example, you might want to remove it from here. 58 | * inputExcludes: ["android/**", "ios/**"], 59 | * 60 | * // override which node gets called and with what additional arguments 61 | * nodeExecutableAndArgs: ["node"] 62 | * 63 | * // supply additional arguments to the packager 64 | * extraPackagerArgs: [] 65 | * ] 66 | */ 67 | 68 | apply from: "../../node_modules/react-native/react.gradle" 69 | 70 | /** 71 | * Set this to true to create two separate APKs instead of one: 72 | * - An APK that only works on ARM devices 73 | * - An APK that only works on x86 devices 74 | * The advantage is the size of the APK is reduced by about 4MB. 75 | * Upload all the APKs to the Play Store and people will download 76 | * the correct one based on the CPU architecture of their device. 77 | */ 78 | def enableSeparateBuildPerCPUArchitecture = false 79 | 80 | /** 81 | * Run Proguard to shrink the Java bytecode in release builds. 82 | */ 83 | def enableProguardInReleaseBuilds = false 84 | 85 | android { 86 | compileSdkVersion 23 87 | buildToolsVersion "23.0.1" 88 | 89 | defaultConfig { 90 | applicationId "com.example" 91 | minSdkVersion 16 92 | targetSdkVersion 22 93 | versionCode 1 94 | versionName "1.0" 95 | ndk { 96 | abiFilters "armeabi-v7a", "x86" 97 | } 98 | } 99 | splits { 100 | abi { 101 | reset() 102 | enable enableSeparateBuildPerCPUArchitecture 103 | universalApk false // If true, also generate a universal APK 104 | include "armeabi-v7a", "x86" 105 | } 106 | } 107 | buildTypes { 108 | release { 109 | minifyEnabled enableProguardInReleaseBuilds 110 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 111 | } 112 | } 113 | // applicationVariants are e.g. debug, release 114 | applicationVariants.all { variant -> 115 | variant.outputs.each { output -> 116 | // For each separate APK per architecture, set a unique version code as described here: 117 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 118 | def versionCodes = ["armeabi-v7a":1, "x86":2] 119 | def abi = output.getFilter(OutputFile.ABI) 120 | if (abi != null) { // null for the universal-debug, universal-release variants 121 | output.versionCodeOverride = 122 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 123 | } 124 | } 125 | } 126 | } 127 | 128 | dependencies { 129 | compile fileTree(dir: "libs", include: ["*.jar"]) 130 | compile "com.android.support:appcompat-v7:23.0.1" 131 | compile "com.facebook.react:react-native:+" // From node_modules 132 | } 133 | 134 | // Run this once to be able to run the application with BUCK 135 | // puts all compile dependencies into folder libs for BUCK to use 136 | task copyDownloadableDepsToLibs(type: Copy) { 137 | from configurations.compile 138 | into 'libs' 139 | } 140 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.react.shell.MainReactPackage; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | protected boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage() 27 | ); 28 | } 29 | }; 30 | 31 | @Override 32 | public ReactNativeHost getReactNativeHost() { 33 | return mReactNativeHost; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liroo/react-native-markdown-syntax/7f8788c90ec3ad82ee17377b65e8a3d8f9b6a437/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liroo/react-native-markdown-syntax/7f8788c90ec3ad82ee17377b65e8a3d8f9b6a437/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liroo/react-native-markdown-syntax/7f8788c90ec3ad82ee17377b65e8a3d8f9b6a437/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liroo/react-native-markdown-syntax/7f8788c90ec3ad82ee17377b65e8a3d8f9b6a437/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liroo/react-native-markdown-syntax/7f8788c90ec3ad82ee17377b65e8a3d8f9b6a437/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = 'debug', 3 | store = 'debug.keystore', 4 | properties = 'debug.keystore.properties', 5 | visibility = [ 6 | 'PUBLIC', 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | AppRegistry, 10 | StyleSheet, 11 | Text, 12 | View 13 | } from 'react-native'; 14 | 15 | export default class example extends Component { 16 | render() { 17 | return ( 18 | 19 | 20 | Welcome to React Native! 21 | 22 | 23 | To get started, edit index.android.js 24 | 25 | 26 | Double tap R on your keyboard to reload,{'\n'} 27 | Shake or press menu button for dev menu 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | justifyContent: 'center', 38 | alignItems: 'center', 39 | backgroundColor: '#F5FCFF', 40 | }, 41 | welcome: { 42 | fontSize: 20, 43 | textAlign: 'center', 44 | margin: 10, 45 | }, 46 | instructions: { 47 | textAlign: 'center', 48 | color: '#333333', 49 | marginBottom: 5, 50 | }, 51 | }); 52 | 53 | AppRegistry.registerComponent('example', () => example); 54 | -------------------------------------------------------------------------------- /example/index.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | AppRegistry, 10 | } from 'react-native'; 11 | import Markdown from 'react-native-markdown-syntax'; 12 | 13 | export default class example extends Component { 14 | render() { 15 | return ( 16 | 17 | {'\n\n'} 18 | Emphasis, aka italics, with *asterisks* or _underscores_.{'\n'} 19 | Strong emphasis, aka bold, with **asterisks** or __underscores__.{'\n'} 20 | Combined emphasis with **asterisks and _underscores_**.{'\n'} 21 | Strikethrough uses two tildes. `Scratch this.`{'\n'} 22 | {' '}Hello World{'\n'} 23 | {'\n'} 24 | > Hello 25 | __yoloeza__ 26 | `e az` az 27 | e 28 | az{'\n'}{'\n'} 29 | - ul{'\n\n​\n\n'}1. ol1{'\n'}2. ol2{'\n'}3. ol3{'\n\n\nHellloooo\n\n'}- ulnew{'\n'}- [ ] checkbox{'\n'} 30 | 31 | ); 32 | } 33 | } 34 | AppRegistry.registerComponent('example', () => example); 35 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 11 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 12 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 13 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 14 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 15 | 00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; }; 16 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 17 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 18 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; 19 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 20 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 21 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 22 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 23 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 24 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 25 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 32 | proxyType = 2; 33 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 34 | remoteInfo = RCTActionSheet; 35 | }; 36 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 39 | proxyType = 2; 40 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 41 | remoteInfo = RCTGeolocation; 42 | }; 43 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 46 | proxyType = 2; 47 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 48 | remoteInfo = RCTImage; 49 | }; 50 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 53 | proxyType = 2; 54 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 55 | remoteInfo = RCTNetwork; 56 | }; 57 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 60 | proxyType = 2; 61 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 62 | remoteInfo = RCTVibration; 63 | }; 64 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { 65 | isa = PBXContainerItemProxy; 66 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; 67 | proxyType = 1; 68 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A; 69 | remoteInfo = example; 70 | }; 71 | 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { 72 | isa = PBXContainerItemProxy; 73 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 74 | proxyType = 2; 75 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 76 | remoteInfo = RCTSettings; 77 | }; 78 | 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { 79 | isa = PBXContainerItemProxy; 80 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 81 | proxyType = 2; 82 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A; 83 | remoteInfo = RCTWebSocket; 84 | }; 85 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 86 | isa = PBXContainerItemProxy; 87 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 88 | proxyType = 2; 89 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 90 | remoteInfo = React; 91 | }; 92 | 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { 93 | isa = PBXContainerItemProxy; 94 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 95 | proxyType = 2; 96 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 97 | remoteInfo = RCTLinking; 98 | }; 99 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 100 | isa = PBXContainerItemProxy; 101 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 102 | proxyType = 2; 103 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 104 | remoteInfo = RCTText; 105 | }; 106 | /* End PBXContainerItemProxy section */ 107 | 108 | /* Begin PBXFileReference section */ 109 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = main.jsbundle; sourceTree = ""; }; 110 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; 111 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; 112 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; 113 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; 114 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; 115 | 00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 116 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 117 | 00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = ""; }; 118 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; 119 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; 120 | 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 121 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; 122 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = example/AppDelegate.m; sourceTree = ""; }; 123 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 124 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = ""; }; 125 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; }; 126 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; 127 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../node_modules/react-native/React/React.xcodeproj; sourceTree = ""; }; 128 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 129 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; 130 | /* End PBXFileReference section */ 131 | 132 | /* Begin PBXFrameworksBuildPhase section */ 133 | 00E356EB1AD99517003FC87E /* Frameworks */ = { 134 | isa = PBXFrameworksBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 142 | isa = PBXFrameworksBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | 146834051AC3E58100842450 /* libReact.a in Frameworks */, 146 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 147 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 148 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 149 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, 150 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 151 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 152 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 153 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 154 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXFrameworksBuildPhase section */ 159 | 160 | /* Begin PBXGroup section */ 161 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 165 | ); 166 | name = Products; 167 | sourceTree = ""; 168 | }; 169 | 00C302B61ABCB90400DB3ED1 /* Products */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | 00C302BC1ABCB91800DB3ED1 /* Products */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 181 | ); 182 | name = Products; 183 | sourceTree = ""; 184 | }; 185 | 00C302D41ABCB9D200DB3ED1 /* Products */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 189 | ); 190 | name = Products; 191 | sourceTree = ""; 192 | }; 193 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 197 | ); 198 | name = Products; 199 | sourceTree = ""; 200 | }; 201 | 00E356EF1AD99517003FC87E /* exampleTests */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 00E356F21AD99517003FC87E /* exampleTests.m */, 205 | 00E356F01AD99517003FC87E /* Supporting Files */, 206 | ); 207 | path = exampleTests; 208 | sourceTree = ""; 209 | }; 210 | 00E356F01AD99517003FC87E /* Supporting Files */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | 00E356F11AD99517003FC87E /* Info.plist */, 214 | ); 215 | name = "Supporting Files"; 216 | sourceTree = ""; 217 | }; 218 | 139105B71AF99BAD00B5F7CC /* Products */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 222 | ); 223 | name = Products; 224 | sourceTree = ""; 225 | }; 226 | 139FDEE71B06529A00C62182 /* Products */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 230 | ); 231 | name = Products; 232 | sourceTree = ""; 233 | }; 234 | 13B07FAE1A68108700A75B9A /* example */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 238 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 239 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 240 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 241 | 13B07FB61A68108700A75B9A /* Info.plist */, 242 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 243 | 13B07FB71A68108700A75B9A /* main.m */, 244 | ); 245 | name = example; 246 | sourceTree = ""; 247 | }; 248 | 146834001AC3E56700842450 /* Products */ = { 249 | isa = PBXGroup; 250 | children = ( 251 | 146834041AC3E56700842450 /* libReact.a */, 252 | ); 253 | name = Products; 254 | sourceTree = ""; 255 | }; 256 | 78C398B11ACF4ADC00677621 /* Products */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 260 | ); 261 | name = Products; 262 | sourceTree = ""; 263 | }; 264 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | 146833FF1AC3E56700842450 /* React.xcodeproj */, 268 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 269 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 270 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 271 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 272 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 273 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 274 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 275 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 276 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 277 | ); 278 | name = Libraries; 279 | sourceTree = ""; 280 | }; 281 | 832341B11AAA6A8300B99B32 /* Products */ = { 282 | isa = PBXGroup; 283 | children = ( 284 | 832341B51AAA6A8300B99B32 /* libRCTText.a */, 285 | ); 286 | name = Products; 287 | sourceTree = ""; 288 | }; 289 | 83CBB9F61A601CBA00E9B192 = { 290 | isa = PBXGroup; 291 | children = ( 292 | 13B07FAE1A68108700A75B9A /* example */, 293 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 294 | 00E356EF1AD99517003FC87E /* exampleTests */, 295 | 83CBBA001A601CBA00E9B192 /* Products */, 296 | ); 297 | indentWidth = 2; 298 | sourceTree = ""; 299 | tabWidth = 2; 300 | }; 301 | 83CBBA001A601CBA00E9B192 /* Products */ = { 302 | isa = PBXGroup; 303 | children = ( 304 | 13B07F961A680F5B00A75B9A /* example.app */, 305 | 00E356EE1AD99517003FC87E /* exampleTests.xctest */, 306 | ); 307 | name = Products; 308 | sourceTree = ""; 309 | }; 310 | /* End PBXGroup section */ 311 | 312 | /* Begin PBXNativeTarget section */ 313 | 00E356ED1AD99517003FC87E /* exampleTests */ = { 314 | isa = PBXNativeTarget; 315 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */; 316 | buildPhases = ( 317 | 00E356EA1AD99517003FC87E /* Sources */, 318 | 00E356EB1AD99517003FC87E /* Frameworks */, 319 | 00E356EC1AD99517003FC87E /* Resources */, 320 | ); 321 | buildRules = ( 322 | ); 323 | dependencies = ( 324 | 00E356F51AD99517003FC87E /* PBXTargetDependency */, 325 | ); 326 | name = exampleTests; 327 | productName = exampleTests; 328 | productReference = 00E356EE1AD99517003FC87E /* exampleTests.xctest */; 329 | productType = "com.apple.product-type.bundle.unit-test"; 330 | }; 331 | 13B07F861A680F5B00A75B9A /* example */ = { 332 | isa = PBXNativeTarget; 333 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; 334 | buildPhases = ( 335 | 13B07F871A680F5B00A75B9A /* Sources */, 336 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 337 | 13B07F8E1A680F5B00A75B9A /* Resources */, 338 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 339 | ); 340 | buildRules = ( 341 | ); 342 | dependencies = ( 343 | ); 344 | name = example; 345 | productName = "Hello World"; 346 | productReference = 13B07F961A680F5B00A75B9A /* example.app */; 347 | productType = "com.apple.product-type.application"; 348 | }; 349 | /* End PBXNativeTarget section */ 350 | 351 | /* Begin PBXProject section */ 352 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 353 | isa = PBXProject; 354 | attributes = { 355 | LastUpgradeCheck = 0610; 356 | ORGANIZATIONNAME = Facebook; 357 | TargetAttributes = { 358 | 00E356ED1AD99517003FC87E = { 359 | CreatedOnToolsVersion = 6.2; 360 | TestTargetID = 13B07F861A680F5B00A75B9A; 361 | }; 362 | }; 363 | }; 364 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */; 365 | compatibilityVersion = "Xcode 3.2"; 366 | developmentRegion = English; 367 | hasScannedForEncodings = 0; 368 | knownRegions = ( 369 | en, 370 | Base, 371 | ); 372 | mainGroup = 83CBB9F61A601CBA00E9B192; 373 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 374 | projectDirPath = ""; 375 | projectReferences = ( 376 | { 377 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; 378 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 379 | }, 380 | { 381 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; 382 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 383 | }, 384 | { 385 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; 386 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 387 | }, 388 | { 389 | ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; 390 | ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 391 | }, 392 | { 393 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; 394 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 395 | }, 396 | { 397 | ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; 398 | ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 399 | }, 400 | { 401 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; 402 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 403 | }, 404 | { 405 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; 406 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 407 | }, 408 | { 409 | ProductGroup = 139FDEE71B06529A00C62182 /* Products */; 410 | ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 411 | }, 412 | { 413 | ProductGroup = 146834001AC3E56700842450 /* Products */; 414 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; 415 | }, 416 | ); 417 | projectRoot = ""; 418 | targets = ( 419 | 13B07F861A680F5B00A75B9A /* example */, 420 | 00E356ED1AD99517003FC87E /* exampleTests */, 421 | ); 422 | }; 423 | /* End PBXProject section */ 424 | 425 | /* Begin PBXReferenceProxy section */ 426 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 427 | isa = PBXReferenceProxy; 428 | fileType = archive.ar; 429 | path = libRCTActionSheet.a; 430 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; 431 | sourceTree = BUILT_PRODUCTS_DIR; 432 | }; 433 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { 434 | isa = PBXReferenceProxy; 435 | fileType = archive.ar; 436 | path = libRCTGeolocation.a; 437 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; 438 | sourceTree = BUILT_PRODUCTS_DIR; 439 | }; 440 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 441 | isa = PBXReferenceProxy; 442 | fileType = archive.ar; 443 | path = libRCTImage.a; 444 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; 445 | sourceTree = BUILT_PRODUCTS_DIR; 446 | }; 447 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 448 | isa = PBXReferenceProxy; 449 | fileType = archive.ar; 450 | path = libRCTNetwork.a; 451 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; 452 | sourceTree = BUILT_PRODUCTS_DIR; 453 | }; 454 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 455 | isa = PBXReferenceProxy; 456 | fileType = archive.ar; 457 | path = libRCTVibration.a; 458 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; 459 | sourceTree = BUILT_PRODUCTS_DIR; 460 | }; 461 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { 462 | isa = PBXReferenceProxy; 463 | fileType = archive.ar; 464 | path = libRCTSettings.a; 465 | remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; 466 | sourceTree = BUILT_PRODUCTS_DIR; 467 | }; 468 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { 469 | isa = PBXReferenceProxy; 470 | fileType = archive.ar; 471 | path = libRCTWebSocket.a; 472 | remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; 473 | sourceTree = BUILT_PRODUCTS_DIR; 474 | }; 475 | 146834041AC3E56700842450 /* libReact.a */ = { 476 | isa = PBXReferenceProxy; 477 | fileType = archive.ar; 478 | path = libReact.a; 479 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; 480 | sourceTree = BUILT_PRODUCTS_DIR; 481 | }; 482 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { 483 | isa = PBXReferenceProxy; 484 | fileType = archive.ar; 485 | path = libRCTLinking.a; 486 | remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; 487 | sourceTree = BUILT_PRODUCTS_DIR; 488 | }; 489 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 490 | isa = PBXReferenceProxy; 491 | fileType = archive.ar; 492 | path = libRCTText.a; 493 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; 494 | sourceTree = BUILT_PRODUCTS_DIR; 495 | }; 496 | /* End PBXReferenceProxy section */ 497 | 498 | /* Begin PBXResourcesBuildPhase section */ 499 | 00E356EC1AD99517003FC87E /* Resources */ = { 500 | isa = PBXResourcesBuildPhase; 501 | buildActionMask = 2147483647; 502 | files = ( 503 | ); 504 | runOnlyForDeploymentPostprocessing = 0; 505 | }; 506 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 507 | isa = PBXResourcesBuildPhase; 508 | buildActionMask = 2147483647; 509 | files = ( 510 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 511 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 512 | ); 513 | runOnlyForDeploymentPostprocessing = 0; 514 | }; 515 | /* End PBXResourcesBuildPhase section */ 516 | 517 | /* Begin PBXShellScriptBuildPhase section */ 518 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 519 | isa = PBXShellScriptBuildPhase; 520 | buildActionMask = 2147483647; 521 | files = ( 522 | ); 523 | inputPaths = ( 524 | ); 525 | name = "Bundle React Native code and images"; 526 | outputPaths = ( 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | shellPath = /bin/sh; 530 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; 531 | showEnvVarsInLog = 1; 532 | }; 533 | /* End PBXShellScriptBuildPhase section */ 534 | 535 | /* Begin PBXSourcesBuildPhase section */ 536 | 00E356EA1AD99517003FC87E /* Sources */ = { 537 | isa = PBXSourcesBuildPhase; 538 | buildActionMask = 2147483647; 539 | files = ( 540 | 00E356F31AD99517003FC87E /* exampleTests.m in Sources */, 541 | ); 542 | runOnlyForDeploymentPostprocessing = 0; 543 | }; 544 | 13B07F871A680F5B00A75B9A /* Sources */ = { 545 | isa = PBXSourcesBuildPhase; 546 | buildActionMask = 2147483647; 547 | files = ( 548 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 549 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 550 | ); 551 | runOnlyForDeploymentPostprocessing = 0; 552 | }; 553 | /* End PBXSourcesBuildPhase section */ 554 | 555 | /* Begin PBXTargetDependency section */ 556 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { 557 | isa = PBXTargetDependency; 558 | target = 13B07F861A680F5B00A75B9A /* example */; 559 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; 560 | }; 561 | /* End PBXTargetDependency section */ 562 | 563 | /* Begin PBXVariantGroup section */ 564 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { 565 | isa = PBXVariantGroup; 566 | children = ( 567 | 13B07FB21A68108700A75B9A /* Base */, 568 | ); 569 | name = LaunchScreen.xib; 570 | path = example; 571 | sourceTree = ""; 572 | }; 573 | /* End PBXVariantGroup section */ 574 | 575 | /* Begin XCBuildConfiguration section */ 576 | 00E356F61AD99517003FC87E /* Debug */ = { 577 | isa = XCBuildConfiguration; 578 | buildSettings = { 579 | BUNDLE_LOADER = "$(TEST_HOST)"; 580 | GCC_PREPROCESSOR_DEFINITIONS = ( 581 | "DEBUG=1", 582 | "$(inherited)", 583 | ); 584 | INFOPLIST_FILE = exampleTests/Info.plist; 585 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 586 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 587 | PRODUCT_NAME = "$(TARGET_NAME)"; 588 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; 589 | }; 590 | name = Debug; 591 | }; 592 | 00E356F71AD99517003FC87E /* Release */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | BUNDLE_LOADER = "$(TEST_HOST)"; 596 | COPY_PHASE_STRIP = NO; 597 | INFOPLIST_FILE = exampleTests/Info.plist; 598 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 599 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 600 | PRODUCT_NAME = "$(TARGET_NAME)"; 601 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; 602 | }; 603 | name = Release; 604 | }; 605 | 13B07F941A680F5B00A75B9A /* Debug */ = { 606 | isa = XCBuildConfiguration; 607 | buildSettings = { 608 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 609 | CURRENT_PROJECT_VERSION = 1; 610 | DEAD_CODE_STRIPPING = NO; 611 | HEADER_SEARCH_PATHS = ( 612 | "$(inherited)", 613 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 614 | "$(SRCROOT)/../node_modules/react-native/React/**", 615 | ); 616 | INFOPLIST_FILE = "example/Info.plist"; 617 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 618 | OTHER_LDFLAGS = ( 619 | "$(inherited)", 620 | "-ObjC", 621 | "-lc++", 622 | ); 623 | PRODUCT_NAME = example; 624 | VERSIONING_SYSTEM = "apple-generic"; 625 | }; 626 | name = Debug; 627 | }; 628 | 13B07F951A680F5B00A75B9A /* Release */ = { 629 | isa = XCBuildConfiguration; 630 | buildSettings = { 631 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 632 | CURRENT_PROJECT_VERSION = 1; 633 | HEADER_SEARCH_PATHS = ( 634 | "$(inherited)", 635 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 636 | "$(SRCROOT)/../node_modules/react-native/React/**", 637 | ); 638 | INFOPLIST_FILE = "example/Info.plist"; 639 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 640 | OTHER_LDFLAGS = ( 641 | "$(inherited)", 642 | "-ObjC", 643 | "-lc++", 644 | ); 645 | PRODUCT_NAME = example; 646 | VERSIONING_SYSTEM = "apple-generic"; 647 | }; 648 | name = Release; 649 | }; 650 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 651 | isa = XCBuildConfiguration; 652 | buildSettings = { 653 | ALWAYS_SEARCH_USER_PATHS = NO; 654 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 655 | CLANG_CXX_LIBRARY = "libc++"; 656 | CLANG_ENABLE_MODULES = YES; 657 | CLANG_ENABLE_OBJC_ARC = YES; 658 | CLANG_WARN_BOOL_CONVERSION = YES; 659 | CLANG_WARN_CONSTANT_CONVERSION = YES; 660 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 661 | CLANG_WARN_EMPTY_BODY = YES; 662 | CLANG_WARN_ENUM_CONVERSION = YES; 663 | CLANG_WARN_INT_CONVERSION = YES; 664 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 665 | CLANG_WARN_UNREACHABLE_CODE = YES; 666 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 667 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 668 | COPY_PHASE_STRIP = NO; 669 | ENABLE_STRICT_OBJC_MSGSEND = YES; 670 | GCC_C_LANGUAGE_STANDARD = gnu99; 671 | GCC_DYNAMIC_NO_PIC = NO; 672 | GCC_OPTIMIZATION_LEVEL = 0; 673 | GCC_PREPROCESSOR_DEFINITIONS = ( 674 | "DEBUG=1", 675 | "$(inherited)", 676 | ); 677 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 678 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 679 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 680 | GCC_WARN_UNDECLARED_SELECTOR = YES; 681 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 682 | GCC_WARN_UNUSED_FUNCTION = YES; 683 | GCC_WARN_UNUSED_VARIABLE = YES; 684 | HEADER_SEARCH_PATHS = ( 685 | "$(inherited)", 686 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 687 | "$(SRCROOT)/../node_modules/react-native/React/**", 688 | ); 689 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 690 | MTL_ENABLE_DEBUG_INFO = YES; 691 | ONLY_ACTIVE_ARCH = YES; 692 | SDKROOT = iphoneos; 693 | }; 694 | name = Debug; 695 | }; 696 | 83CBBA211A601CBA00E9B192 /* Release */ = { 697 | isa = XCBuildConfiguration; 698 | buildSettings = { 699 | ALWAYS_SEARCH_USER_PATHS = NO; 700 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 701 | CLANG_CXX_LIBRARY = "libc++"; 702 | CLANG_ENABLE_MODULES = YES; 703 | CLANG_ENABLE_OBJC_ARC = YES; 704 | CLANG_WARN_BOOL_CONVERSION = YES; 705 | CLANG_WARN_CONSTANT_CONVERSION = YES; 706 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 707 | CLANG_WARN_EMPTY_BODY = YES; 708 | CLANG_WARN_ENUM_CONVERSION = YES; 709 | CLANG_WARN_INT_CONVERSION = YES; 710 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 711 | CLANG_WARN_UNREACHABLE_CODE = YES; 712 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 713 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 714 | COPY_PHASE_STRIP = YES; 715 | ENABLE_NS_ASSERTIONS = NO; 716 | ENABLE_STRICT_OBJC_MSGSEND = YES; 717 | GCC_C_LANGUAGE_STANDARD = gnu99; 718 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 719 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 720 | GCC_WARN_UNDECLARED_SELECTOR = YES; 721 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 722 | GCC_WARN_UNUSED_FUNCTION = YES; 723 | GCC_WARN_UNUSED_VARIABLE = YES; 724 | HEADER_SEARCH_PATHS = ( 725 | "$(inherited)", 726 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 727 | "$(SRCROOT)/../node_modules/react-native/React/**", 728 | ); 729 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 730 | MTL_ENABLE_DEBUG_INFO = NO; 731 | SDKROOT = iphoneos; 732 | VALIDATE_PRODUCT = YES; 733 | }; 734 | name = Release; 735 | }; 736 | /* End XCBuildConfiguration section */ 737 | 738 | /* Begin XCConfigurationList section */ 739 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */ = { 740 | isa = XCConfigurationList; 741 | buildConfigurations = ( 742 | 00E356F61AD99517003FC87E /* Debug */, 743 | 00E356F71AD99517003FC87E /* Release */, 744 | ); 745 | defaultConfigurationIsVisible = 0; 746 | defaultConfigurationName = Release; 747 | }; 748 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = { 749 | isa = XCConfigurationList; 750 | buildConfigurations = ( 751 | 13B07F941A680F5B00A75B9A /* Debug */, 752 | 13B07F951A680F5B00A75B9A /* Release */, 753 | ); 754 | defaultConfigurationIsVisible = 0; 755 | defaultConfigurationName = Release; 756 | }; 757 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = { 758 | isa = XCConfigurationList; 759 | buildConfigurations = ( 760 | 83CBBA201A601CBA00E9B192 /* Debug */, 761 | 83CBBA211A601CBA00E9B192 /* Release */, 762 | ); 763 | defaultConfigurationIsVisible = 0; 764 | defaultConfigurationName = Release; 765 | }; 766 | /* End XCConfigurationList section */ 767 | }; 768 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 769 | } 770 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTBundleURLProvider.h" 13 | #import "RCTRootView.h" 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"example" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /example/ios/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTLog.h" 14 | #import "RCTRootView.h" 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface exampleTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation exampleTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "react": "15.3.2", 11 | "react-native": "0.36.0", 12 | "react-native-markdown-syntax": "Liroo/react-native-markdown-syntax" 13 | }, 14 | "jest": { 15 | "preset": "jest-react-native" 16 | }, 17 | "devDependencies": { 18 | "babel-jest": "16.0.0", 19 | "babel-preset-react-native": "1.9.0", 20 | "jest": "16.0.2", 21 | "jest-react-native": "16.0.0", 22 | "react-test-renderer": "15.3.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // super simple module for the most common nodejs use case. 2 | exports.markdown = require("./md"); 3 | exports.parse = exports.markdown.toHTML; 4 | -------------------------------------------------------------------------------- /lib/md.js: -------------------------------------------------------------------------------- 1 | // Released under MIT license 2 | // Copyright (c) 2009-2010 Dominic Baggott 3 | // Copyright (c) 2009-2010 Ash Berlin 4 | // Copyright (c) 2011 Christoph Dorn (http://www.christophdorn.com) 5 | // Copyright (c) 2016 Pierre Monge 6 | /*jshint browser:true, devel:true */ 7 | 8 | const ReactHelpers = require('./react_helpers'); 9 | (function( expose ) { 10 | 11 | /** 12 | * class Markdown 13 | * 14 | * Markdown processing in Javascript done right. We have very particular views 15 | * on what constitutes 'right' which include: 16 | * 17 | * - produces well-formed HTML (this means that em and strong nesting is 18 | * important) 19 | * 20 | * - has an intermediate representation to allow processing of parsed data (We 21 | * in fact have two, both as [JsonML]: a markdown tree and an HTML tree). 22 | * 23 | * - is easily extensible to add new dialects without having to rewrite the 24 | * entire parsing mechanics 25 | * 26 | * - has a good test suite 27 | * 28 | * This implementation fulfills all of these (except that the test suite could 29 | * do with expanding to automatically run all the fixtures from other Markdown 30 | * implementations.) 31 | * 32 | * ##### Intermediate Representation 33 | * 34 | * *TODO* Talk about this :) Its JsonML, but document the node names we use. 35 | * 36 | * [JsonML]: http://jsonml.org/ "JSON Markup Language" 37 | **/ 38 | var Markdown = expose.Markdown = function(dialect) { 39 | switch (typeof dialect) { 40 | case "undefined": 41 | this.dialect = Markdown.dialects.Gruber; 42 | break; 43 | case "object": 44 | this.dialect = dialect; 45 | break; 46 | default: 47 | if ( dialect in Markdown.dialects ) { 48 | this.dialect = Markdown.dialects[dialect]; 49 | } 50 | else { 51 | throw new Error("Unknown Markdown dialect '" + String(dialect) + "'"); 52 | } 53 | break; 54 | } 55 | this.em_state = []; 56 | this.strong_state = []; 57 | this.debug_indent = ""; 58 | }; 59 | 60 | /** 61 | * parse( markdown, [dialect] ) -> JsonML 62 | * - markdown (String): markdown string to parse 63 | * - dialect (String | Dialect): the dialect to use, defaults to gruber 64 | * 65 | * Parse `markdown` and return a markdown document as a Markdown.JsonML tree. 66 | **/ 67 | expose.parse = function( source, dialect ) { 68 | // dialect will default if undefined 69 | var md = new Markdown( dialect ); 70 | return md.toTree( source ); 71 | }; 72 | 73 | /** 74 | * toReact( markdown, [dialect] ) -> String 75 | * toReact( md_tree ) -> String 76 | * - markdown (String): markdown string to parse 77 | * - md_tree (Markdown.JsonML): parsed markdown tree 78 | * 79 | * Take markdown (either as a string or as a JsonML tree) and run it through 80 | * [[toHTMLTree]] then turn it into a well-formated HTML fragment. 81 | **/ 82 | expose.toReact = function toReact( source , dialect , options ) { 83 | var input = expose.toHTMLTree( source , dialect , options ); 84 | 85 | return expose.renderJsonML( input ); 86 | }; 87 | 88 | /** 89 | * toHTMLTree( markdown, [dialect] ) -> JsonML 90 | * toHTMLTree( md_tree ) -> JsonML 91 | * - markdown (String): markdown string to parse 92 | * - dialect (String | Dialect): the dialect to use, defaults to gruber 93 | * - md_tree (Markdown.JsonML): parsed markdown tree 94 | * 95 | * Turn markdown into HTML, represented as a JsonML tree. If a string is given 96 | * to this function, it is first parsed into a markdown tree by calling 97 | * [[parse]]. 98 | **/ 99 | expose.toHTMLTree = function toHTMLTree( input, dialect , options ) { 100 | // convert string input to an MD tree 101 | if ( typeof input ==="string" ) input = this.parse( input, dialect ); 102 | 103 | // Now convert the MD tree to an HTML tree 104 | 105 | // remove references from the tree 106 | var attrs = extract_attr( input ), 107 | refs = {}; 108 | 109 | if ( attrs && attrs.references ) { 110 | refs = attrs.references; 111 | } 112 | 113 | var html = convert_tree_to_html( input, refs , options ); 114 | merge_text_nodes( html ); 115 | return html; 116 | }; 117 | 118 | // For Spidermonkey based engines 119 | function mk_block_toSource() { 120 | return "Markdown.mk_block( " + 121 | uneval(this.toString()) + 122 | ", " + 123 | uneval(this.trailing) + 124 | ", " + 125 | uneval(this.lineNumber) + 126 | " )"; 127 | } 128 | 129 | // node 130 | function mk_block_inspect() { 131 | var util = require("util"); 132 | return "Markdown.mk_block( " + 133 | util.inspect(this.toString()) + 134 | ", " + 135 | util.inspect(this.trailing) + 136 | ", " + 137 | util.inspect(this.lineNumber) + 138 | " )"; 139 | 140 | } 141 | 142 | var mk_block = Markdown.mk_block = function(block, trail, line) { 143 | // Be helpful for default case in tests. 144 | if ( arguments.length == 1 ) trail = "\n\n"; 145 | 146 | var s = new String(block); 147 | s.trailing = trail; 148 | // To make it clear its not just a string 149 | s.inspect = mk_block_inspect; 150 | s.toSource = mk_block_toSource; 151 | 152 | if ( line != undefined ) 153 | s.lineNumber = line; 154 | 155 | return s; 156 | }; 157 | 158 | function count_lines( str ) { 159 | var n = 0, i = -1; 160 | while ( ( i = str.indexOf("\n", i + 1) ) !== -1 ) n++; 161 | return n; 162 | } 163 | 164 | // Internal - split source into rough blocks 165 | Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) { 166 | input = input.replace(/(\r\n|\n|\r)/g, "\n"); 167 | // [\s\S] matches _anything_ (newline or space) 168 | // [^] is equivalent but doesn't work in IEs. 169 | var re = /([\s\S]+?)($|\n#|\n(?:\s*\n|$)+)/g, 170 | blocks = [], 171 | m; 172 | 173 | var line_no = 1; 174 | 175 | if ( ( m = /^(\s*\n)/.exec(input) ) != null ) { 176 | // skip (but count) leading blank lines 177 | line_no += count_lines( m[0] ); 178 | re.lastIndex = m[0].length; 179 | } 180 | 181 | while ( ( m = re.exec(input) ) !== null ) { 182 | if (m[2] == "\n#") { 183 | m[2] = "\n"; 184 | re.lastIndex--; 185 | } 186 | blocks.push( mk_block( m[1], m[2], line_no ) ); 187 | line_no += count_lines( m[0] ); 188 | } 189 | 190 | return blocks; 191 | }; 192 | 193 | /** 194 | * Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ] 195 | * - block (String): the block to process 196 | * - next (Array): the following blocks 197 | * 198 | * Process `block` and return an array of JsonML nodes representing `block`. 199 | * 200 | * It does this by asking each block level function in the dialect to process 201 | * the block until one can. Succesful handling is indicated by returning an 202 | * array (with zero or more JsonML nodes), failure by a false value. 203 | * 204 | * Blocks handlers are responsible for calling [[Markdown#processInline]] 205 | * themselves as appropriate. 206 | * 207 | * If the blocks were split incorrectly or adjacent blocks need collapsing you 208 | * can adjust `next` in place using shift/splice etc. 209 | * 210 | * If any of this default behaviour is not right for the dialect, you can 211 | * define a `__call__` method on the dialect that will get invoked to handle 212 | * the block processing. 213 | */ 214 | Markdown.prototype.processBlock = function processBlock( block, next ) { 215 | var cbs = this.dialect.block, 216 | ord = cbs.__order__; 217 | 218 | if ( "__call__" in cbs ) { 219 | return cbs.__call__.call(this, block, next); 220 | } 221 | 222 | for ( var i = 0; i < ord.length; i++ ) { 223 | //D:this.debug( "Testing", ord[i] ); 224 | var res = cbs[ ord[i] ].call( this, block, next ); 225 | if ( res ) { 226 | //D:this.debug(" matched"); 227 | if ( !isArray(res) || ( res.length > 0 && !( isArray(res[0]) ) ) ) 228 | this.debug(ord[i], "didn't return a proper array"); 229 | //D:this.debug( "" ); 230 | return res; 231 | } 232 | } 233 | 234 | // Uhoh! no match! Should we throw an error? 235 | return []; 236 | }; 237 | 238 | Markdown.prototype.processInline = function processInline( block ) { 239 | return this.dialect.inline.__call__.call( this, String( block ) ); 240 | }; 241 | 242 | /** 243 | * Markdown#toTree( source ) -> JsonML 244 | * - source (String): markdown source to parse 245 | * 246 | * Parse `source` into a JsonML tree representing the markdown document. 247 | **/ 248 | // custom_tree means set this.tree to `custom_tree` and restore old value on return 249 | Markdown.prototype.toTree = function toTree( source, custom_root ) { 250 | var blocks = source instanceof Array ? source : this.split_blocks( source ); 251 | 252 | // Make tree a member variable so its easier to mess with in extensions 253 | var old_tree = this.tree; 254 | try { 255 | this.tree = custom_root || this.tree || [ "markdown" ]; 256 | 257 | blocks: 258 | while ( blocks.length ) { 259 | var b = this.processBlock( blocks.shift(), blocks ); 260 | 261 | // Reference blocks and the like won't return any content 262 | if ( !b.length ) continue blocks; 263 | 264 | this.tree.push.apply( this.tree, b ); 265 | } 266 | return this.tree; 267 | } 268 | finally { 269 | if ( custom_root ) { 270 | this.tree = old_tree; 271 | } 272 | } 273 | }; 274 | 275 | // Noop by default 276 | Markdown.prototype.debug = function () { 277 | var args = Array.prototype.slice.call( arguments); 278 | args.unshift(this.debug_indent); 279 | if ( typeof print !== "undefined" ) 280 | print.apply( print, args ); 281 | if ( typeof console !== "undefined" && typeof console.log !== "undefined" ) 282 | console.log.apply( null, args ); 283 | } 284 | 285 | Markdown.prototype.loop_re_over_block = function( re, block, cb ) { 286 | // Dont use /g regexps with this 287 | var m, 288 | b = block.valueOf(); 289 | 290 | while ( b.length && (m = re.exec(b) ) != null ) { 291 | b = b.substr( m[0].length ); 292 | cb.call(this, m); 293 | } 294 | return b; 295 | }; 296 | 297 | /** 298 | * Markdown.dialects 299 | * 300 | * Namespace of built-in dialects. 301 | **/ 302 | Markdown.dialects = {}; 303 | 304 | /** 305 | * Markdown.dialects.Gruber 306 | * 307 | * The default dialect that follows the rules set out by John Gruber's 308 | * markdown.pl as closely as possible. Well actually we follow the behaviour of 309 | * that script which in some places is not exactly what the syntax web page 310 | * says. 311 | **/ 312 | Markdown.dialects.Gruber = { 313 | block: { 314 | atxHeader: function atxHeader( block, next ) { 315 | var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ ); 316 | 317 | if ( !m ) return undefined; 318 | 319 | var header = [ "header", { level: m[ 1 ].length } ]; 320 | Array.prototype.push.apply(header, this.processInline(m[ 2 ])); 321 | 322 | if ( m[0].length < block.length ) 323 | next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); 324 | 325 | return [ header ]; 326 | }, 327 | 328 | setextHeader: function setextHeader( block, next ) { 329 | var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ ); 330 | 331 | if ( !m ) return undefined; 332 | 333 | var level = ( m[ 2 ] === "=" ) ? 1 : 2; 334 | var header = [ "header", { level : level }, m[ 1 ] ]; 335 | 336 | if ( m[0].length < block.length ) 337 | next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); 338 | 339 | return [ header ]; 340 | }, 341 | 342 | code: function code( block, next ) { 343 | // | Foo 344 | // |bar 345 | // should be a code block followed by a paragraph. Fun 346 | // 347 | // There might also be adjacent code block to merge. 348 | 349 | var ret = [], 350 | re = /^(?: {0,3}\t| {4})(.*)\n?/, 351 | lines; 352 | 353 | // 4 spaces + content 354 | if ( !block.match( re ) ) return undefined; 355 | 356 | block_search: 357 | do { 358 | // Now pull out the rest of the lines 359 | var b = this.loop_re_over_block( 360 | re, block.valueOf(), function( m ) { ret.push( m[1] ); } ); 361 | 362 | if ( b.length ) { 363 | // Case alluded to in first comment. push it back on as a new block 364 | next.unshift( mk_block(b, block.trailing) ); 365 | break block_search; 366 | } 367 | else if ( next.length ) { 368 | // Check the next block - it might be code too 369 | if ( !next[0].match( re ) ) break block_search; 370 | 371 | // Pull how how many blanks lines follow - minus two to account for .join 372 | ret.push ( block.trailing.replace(/[^\n]/g, "").substring(2) ); 373 | 374 | block = next.shift(); 375 | } 376 | else { 377 | break block_search; 378 | } 379 | } while ( true ); 380 | 381 | return [ [ "code_block", ret.join("\n") ] ]; 382 | }, 383 | 384 | horizRule: function horizRule( block, next ) { 385 | // this needs to find any hr in the block to handle abutting blocks 386 | var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ ); 387 | 388 | if ( !m ) { 389 | return undefined; 390 | } 391 | 392 | var jsonml = [ [ "hr" ] ]; 393 | 394 | // if there's a leading abutting block, process it 395 | if ( m[ 1 ] ) { 396 | jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) ); 397 | } 398 | 399 | // if there's a trailing abutting block, stick it into next 400 | if ( m[ 3 ] ) { 401 | next.unshift( mk_block( m[ 3 ] ) ); 402 | } 403 | 404 | return jsonml; 405 | }, 406 | 407 | // There are two types of lists. Tight and loose. Tight lists have no whitespace 408 | // between the items (and result in text just in the
  • ) and loose lists, 409 | // which have an empty line between list items, resulting in (one or more) 410 | // paragraphs inside the
  • . 411 | // 412 | // There are all sorts weird edge cases about the original markdown.pl's 413 | // handling of lists: 414 | // 415 | // * Nested lists are supposed to be indented by four chars per level. But 416 | // if they aren't, you can get a nested list by indenting by less than 417 | // four so long as the indent doesn't match an indent of an existing list 418 | // item in the 'nest stack'. 419 | // 420 | // * The type of the list (bullet or number) is controlled just by the 421 | // first item at the indent. Subsequent changes are ignored unless they 422 | // are for nested lists 423 | // 424 | lists: (function( ) { 425 | // Use a closure to hide a few variables. 426 | var any_list = "[*+-]|\\d+\\.", 427 | bullet_list = /[*+-]/, 428 | number_list = /\d+\./, 429 | // Capture leading indent as it matters for determining nested lists. 430 | is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ), 431 | indent_re = "(?: {0,3}\t| {4})"; 432 | 433 | // TODO: Cache this regexp for certain depths. 434 | // Create a regexp suitable for matching an li for a given stack depth 435 | function regex_for_depth( depth ) { 436 | 437 | return new RegExp( 438 | // m[1] = indent, m[2] = list_type 439 | "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" + 440 | // m[3] = cont 441 | "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})" 442 | ); 443 | } 444 | function expand_tab( input ) { 445 | return input.replace( / {0,3}\t/g, " " ); 446 | } 447 | 448 | // Add inline content `inline` to `li`. inline comes from processInline 449 | // so is an array of content 450 | function add(li, loose, inline, nl) { 451 | if ( loose ) { 452 | li.push( [ "para" ].concat(inline) ); 453 | return; 454 | } 455 | // Hmmm, should this be any block level element or just paras? 456 | var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para" 457 | ? li[li.length -1] 458 | : li; 459 | 460 | // If there is already some content in this list, add the new line in 461 | if ( nl && li.length > 1 ) inline.unshift(nl); 462 | 463 | for ( var i = 0; i < inline.length; i++ ) { 464 | var what = inline[i], 465 | is_str = typeof what == "string"; 466 | if ( is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) { 467 | add_to[ add_to.length-1 ] += what; 468 | } 469 | else { 470 | add_to.push( what ); 471 | } 472 | } 473 | } 474 | 475 | // contained means have an indent greater than the current one. On 476 | // *every* line in the block 477 | function get_contained_blocks( depth, blocks ) { 478 | 479 | var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ), 480 | replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"), 481 | ret = []; 482 | 483 | while ( blocks.length > 0 ) { 484 | if ( re.exec( blocks[0] ) ) { 485 | var b = blocks.shift(), 486 | // Now remove that indent 487 | x = b.replace( replace, ""); 488 | 489 | ret.push( mk_block( x, b.trailing, b.lineNumber ) ); 490 | } 491 | else { 492 | break; 493 | } 494 | } 495 | return ret; 496 | } 497 | 498 | // passed to stack.forEach to turn list items up the stack into paras 499 | function paragraphify(s, i, stack) { 500 | var list = s.list; 501 | var last_li = list[list.length-1]; 502 | 503 | if ( last_li[1] instanceof Array && last_li[1][0] == "para" ) { 504 | return; 505 | } 506 | if ( i + 1 == stack.length ) { 507 | // Last stack frame 508 | // Keep the same array, but replace the contents 509 | last_li.push( ["para"].concat( last_li.splice(1, last_li.length - 1) ) ); 510 | } 511 | else { 512 | var sublist = last_li.pop(); 513 | last_li.push( ["para"].concat( last_li.splice(1, last_li.length - 1) ), sublist ); 514 | } 515 | } 516 | 517 | // The matcher function 518 | return function( block, next ) { 519 | var m = block.match( is_list_re ); 520 | if ( !m ) return undefined; 521 | 522 | function make_list( m ) { 523 | var list = bullet_list.exec( m[2] ) 524 | ? ["bulletlist"] 525 | : ["numberlist"]; 526 | 527 | stack.push( { list: list, indent: m[1] } ); 528 | return list; 529 | } 530 | 531 | 532 | var stack = [], // Stack of lists for nesting. 533 | list = make_list( m ), 534 | last_li, 535 | loose = false, 536 | ret = [ stack[0].list ], 537 | i; 538 | 539 | // Loop to search over block looking for inner block elements and loose lists 540 | loose_search: 541 | while ( true ) { 542 | // Split into lines preserving new lines at end of line 543 | var lines = block.split( /(?=\n)/ ); 544 | 545 | // We have to grab all lines for a li and call processInline on them 546 | // once as there are some inline things that can span lines. 547 | var li_accumulate = ""; 548 | 549 | // Loop over the lines in this block looking for tight lists. 550 | tight_search: 551 | for ( var line_no = 0; line_no < lines.length; line_no++ ) { 552 | var nl = "", 553 | l = lines[line_no].replace(/^\n/, function(n) { nl = n; return ""; }); 554 | 555 | // TODO: really should cache this 556 | var line_re = regex_for_depth( stack.length ); 557 | 558 | m = l.match( line_re ); 559 | //print( "line:", uneval(l), "\nline match:", uneval(m) ); 560 | 561 | // We have a list item 562 | if ( m[1] !== undefined ) { 563 | // Process the previous list item, if any 564 | if ( li_accumulate.length ) { 565 | add( last_li, loose, this.processInline( li_accumulate ), nl ); 566 | // Loose mode will have been dealt with. Reset it 567 | loose = false; 568 | li_accumulate = ""; 569 | } 570 | 571 | m[1] = expand_tab( m[1] ); 572 | var wanted_depth = Math.floor(m[1].length/4)+1; 573 | //print( "want:", wanted_depth, "stack:", stack.length); 574 | if ( wanted_depth > stack.length ) { 575 | // Deep enough for a nested list outright 576 | //print ( "new nested list" ); 577 | list = make_list( m ); 578 | last_li.push( list ); 579 | last_li = list[1] = [ "listitem" ]; 580 | } 581 | else { 582 | // We aren't deep enough to be strictly a new level. This is 583 | // where Md.pl goes nuts. If the indent matches a level in the 584 | // stack, put it there, else put it one deeper then the 585 | // wanted_depth deserves. 586 | var found = false; 587 | for ( i = 0; i < stack.length; i++ ) { 588 | if ( stack[ i ].indent != m[1] ) continue; 589 | list = stack[ i ].list; 590 | stack.splice( i+1, stack.length - (i+1) ); 591 | found = true; 592 | break; 593 | } 594 | 595 | if (!found) { 596 | //print("not found. l:", uneval(l)); 597 | wanted_depth++; 598 | if ( wanted_depth <= stack.length ) { 599 | stack.splice(wanted_depth, stack.length - wanted_depth); 600 | //print("Desired depth now", wanted_depth, "stack:", stack.length); 601 | list = stack[wanted_depth-1].list; 602 | //print("list:", uneval(list) ); 603 | } 604 | else { 605 | //print ("made new stack for messy indent"); 606 | list = make_list(m); 607 | last_li.push(list); 608 | } 609 | } 610 | 611 | //print( uneval(list), "last", list === stack[stack.length-1].list ); 612 | last_li = [ "listitem" ]; 613 | list.push(last_li); 614 | } // end depth of shenegains 615 | nl = ""; 616 | } 617 | 618 | // Add content 619 | if ( l.length > m[0].length ) { 620 | li_accumulate += nl + l.substr( m[0].length ); 621 | } 622 | } // tight_search 623 | 624 | if ( li_accumulate.length ) { 625 | add( last_li, loose, this.processInline( li_accumulate ), nl ); 626 | // Loose mode will have been dealt with. Reset it 627 | loose = false; 628 | li_accumulate = ""; 629 | } 630 | 631 | // Look at the next block - we might have a loose list. Or an extra 632 | // paragraph for the current li 633 | var contained = get_contained_blocks( stack.length, next ); 634 | 635 | // Deal with code blocks or properly nested lists 636 | if ( contained.length > 0 ) { 637 | // Make sure all listitems up the stack are paragraphs 638 | forEach( stack, paragraphify, this); 639 | 640 | last_li.push.apply( last_li, this.toTree( contained, [] ) ); 641 | } 642 | 643 | var next_block = next[0] && next[0].valueOf() || ""; 644 | 645 | if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) { 646 | block = next.shift(); 647 | 648 | // Check for an HR following a list: features/lists/hr_abutting 649 | var hr = this.dialect.block.horizRule( block, next ); 650 | 651 | if ( hr ) { 652 | ret.push.apply(ret, hr); 653 | break; 654 | } 655 | 656 | // Make sure all listitems up the stack are paragraphs 657 | forEach( stack, paragraphify, this); 658 | 659 | loose = true; 660 | continue loose_search; 661 | } 662 | break; 663 | } // loose_search 664 | 665 | return ret; 666 | }; 667 | })(), 668 | 669 | blockquote: function blockquote( block, next ) { 670 | if ( !block.match( /^>/m ) ) 671 | return undefined; 672 | 673 | var jsonml = []; 674 | 675 | // separate out the leading abutting block, if any. I.e. in this case: 676 | // 677 | // a 678 | // > b 679 | // 680 | if ( block[ 0 ] != ">" ) { 681 | var lines = block.split( /\n/ ), 682 | prev = [], 683 | line_no = block.lineNumber; 684 | 685 | // keep shifting lines until you find a crotchet 686 | while ( lines.length && lines[ 0 ][ 0 ] != ">" ) { 687 | prev.push( lines.shift() ); 688 | line_no++; 689 | } 690 | 691 | var abutting = mk_block( prev.join( "\n" ), "\n", block.lineNumber ); 692 | jsonml.push.apply( jsonml, this.processBlock( abutting, [] ) ); 693 | // reassemble new block of just block quotes! 694 | block = mk_block( lines.join( "\n" ), block.trailing, line_no ); 695 | } 696 | 697 | 698 | // if the next block is also a blockquote merge it in 699 | while ( next.length && next[ 0 ][ 0 ] == ">" ) { 700 | var b = next.shift(); 701 | block = mk_block( block + block.trailing + b, b.trailing, block.lineNumber ); 702 | } 703 | 704 | // Strip off the leading "> " and re-process as a block. 705 | var input = block.replace( /^> ?/gm, "" ), 706 | old_tree = this.tree, 707 | processedBlock = this.toTree( input, [ "blockquote" ] ), 708 | attr = extract_attr( processedBlock ); 709 | 710 | // If any link references were found get rid of them 711 | if ( attr && attr.references ) { 712 | delete attr.references; 713 | // And then remove the attribute object if it's empty 714 | if ( isEmpty( attr ) ) { 715 | processedBlock.splice( 1, 1 ); 716 | } 717 | } 718 | 719 | jsonml.push( processedBlock ); 720 | return jsonml; 721 | }, 722 | 723 | referenceDefn: function referenceDefn( block, next) { 724 | var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/; 725 | // interesting matches are [ , ref_id, url, , title, title ] 726 | 727 | if ( !block.match(re) ) 728 | return undefined; 729 | 730 | // make an attribute node if it doesn't exist 731 | if ( !extract_attr( this.tree ) ) { 732 | this.tree.splice( 1, 0, {} ); 733 | } 734 | 735 | var attrs = extract_attr( this.tree ); 736 | 737 | // make a references hash if it doesn't exist 738 | if ( attrs.references === undefined ) { 739 | attrs.references = {}; 740 | } 741 | 742 | var b = this.loop_re_over_block(re, block, function( m ) { 743 | 744 | if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" ) 745 | m[2] = m[2].substring( 1, m[2].length - 1 ); 746 | 747 | var ref = attrs.references[ m[1].toLowerCase() ] = { 748 | href: m[2] 749 | }; 750 | 751 | if ( m[4] !== undefined ) 752 | ref.title = m[4]; 753 | else if ( m[5] !== undefined ) 754 | ref.title = m[5]; 755 | 756 | } ); 757 | 758 | if ( b.length ) 759 | next.unshift( mk_block( b, block.trailing ) ); 760 | 761 | return []; 762 | }, 763 | 764 | para: function para( block, next ) { 765 | // everything's a para! 766 | return [ ["para"].concat( this.processInline( block ) ) ]; 767 | } 768 | } 769 | }; 770 | 771 | Markdown.dialects.Gruber.inline = { 772 | 773 | __oneElement__: function oneElement( text, patterns_or_re, previous_nodes ) { 774 | var m, 775 | res, 776 | lastIndex = 0; 777 | 778 | patterns_or_re = patterns_or_re || this.dialect.inline.__patterns__; 779 | var re = new RegExp( "([\\s\\S]*?)(" + (patterns_or_re.source || patterns_or_re) + ")" ); 780 | 781 | m = re.exec( text ); 782 | if (!m) { 783 | // Just boring text 784 | return [ text.length, text ]; 785 | } 786 | else if ( m[1] ) { 787 | // Some un-interesting text matched. Return that first 788 | return [ m[1].length, m[1] ]; 789 | } 790 | 791 | var res; 792 | if ( m[2] in this.dialect.inline ) { 793 | res = this.dialect.inline[ m[2] ].call( 794 | this, 795 | text.substr( m.index ), m, previous_nodes || [] ); 796 | } 797 | // Default for now to make dev easier. just slurp special and output it. 798 | res = res || [ m[2].length, m[2] ]; 799 | return res; 800 | }, 801 | 802 | __call__: function inline( text, patterns ) { 803 | 804 | var out = [], 805 | res; 806 | 807 | function add(x) { 808 | //D:self.debug(" adding output", uneval(x)); 809 | if ( typeof x == "string" && typeof out[out.length-1] == "string" ) 810 | out[ out.length-1 ] += x; 811 | else 812 | out.push(x); 813 | } 814 | 815 | while ( text.length > 0 ) { 816 | res = this.dialect.inline.__oneElement__.call(this, text, patterns, out ); 817 | text = text.substr( res.shift() ); 818 | forEach(res, add ) 819 | } 820 | 821 | return out; 822 | }, 823 | 824 | // These characters are intersting elsewhere, so have rules for them so that 825 | // chunks of plain text blocks don't include them 826 | "]": function () {}, 827 | "}": function () {}, 828 | 829 | __escape__ : /^\\[\\`\*_{}\[\]()#\+.!\-]/, 830 | 831 | "\\": function escaped( text ) { 832 | // [ length of input processed, node/children to add... ] 833 | // Only esacape: \ ` * _ { } [ ] ( ) # * + - . ! 834 | if ( this.dialect.inline.__escape__.exec( text ) ) 835 | return [ 2, text.charAt( 1 ) ]; 836 | else 837 | // Not an esacpe 838 | return [ 1, "\\" ]; 839 | }, 840 | 841 | "![": function image( text ) { 842 | 843 | // Unlike images, alt text is plain text only. no other elements are 844 | // allowed in there 845 | 846 | // ![Alt text](/path/to/img.jpg "Optional title") 847 | // 1 2 3 4 <--- captures 848 | var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*([^")]*?)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); 849 | 850 | if ( m ) { 851 | if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" ) 852 | m[2] = m[2].substring( 1, m[2].length - 1 ); 853 | 854 | m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; 855 | 856 | var attrs = { alt: m[1], href: m[2] || "" }; 857 | if ( m[4] !== undefined) 858 | attrs.title = m[4]; 859 | 860 | return [ m[0].length, [ "img", attrs ] ]; 861 | } 862 | 863 | // ![Alt text][id] 864 | m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ ); 865 | 866 | if ( m ) { 867 | // We can't check if the reference is known here as it likely wont be 868 | // found till after. Check it in md tree->hmtl tree conversion 869 | return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), original: m[0] } ] ]; 870 | } 871 | 872 | // Just consume the '![' 873 | return [ 2, "![" ]; 874 | }, 875 | 876 | "[": function link( text ) { 877 | 878 | var orig = String(text); 879 | // Inline content is possible inside `link text` 880 | var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), "]" ); 881 | 882 | // No closing ']' found. Just consume the [ 883 | if ( !res ) return [ 1, "[" ]; 884 | 885 | var consumed = 1 + res[ 0 ], 886 | children = res[ 1 ], 887 | link, 888 | attrs; 889 | 890 | // At this point the first [...] has been parsed. See what follows to find 891 | // out which kind of link we are (reference or direct url) 892 | text = text.substr( consumed ); 893 | 894 | // [link text](/path/to/img.jpg "Optional title") 895 | // 1 2 3 <--- captures 896 | // This will capture up to the last paren in the block. We then pull 897 | // back based on if there a matching ones in the url 898 | // ([here](/url/(test)) 899 | // The parens have to be balanced 900 | var m = text.match( /^\s*\([ \t]*([^"']*)(?:[ \t]+(["'])(.*?)\2)?[ \t]*\)/ ); 901 | if ( m ) { 902 | var url = m[1]; 903 | consumed += m[0].length; 904 | 905 | if ( url && url[0] == "<" && url[url.length-1] == ">" ) 906 | url = url.substring( 1, url.length - 1 ); 907 | 908 | // If there is a title we don't have to worry about parens in the url 909 | if ( !m[3] ) { 910 | var open_parens = 1; // One open that isn't in the capture 911 | for ( var len = 0; len < url.length; len++ ) { 912 | switch ( url[len] ) { 913 | case "(": 914 | open_parens++; 915 | break; 916 | case ")": 917 | if ( --open_parens == 0) { 918 | consumed -= url.length - len; 919 | url = url.substring(0, len); 920 | } 921 | break; 922 | } 923 | } 924 | } 925 | 926 | // Process escapes only 927 | url = this.dialect.inline.__call__.call( this, url, /\\/ )[0]; 928 | 929 | attrs = { href: url || "" }; 930 | if ( m[3] !== undefined) 931 | attrs.title = m[3]; 932 | 933 | link = [ "link", attrs ].concat( children ); 934 | return [ consumed, link ]; 935 | } 936 | 937 | // [Alt text][id] 938 | // [Alt text] [id] 939 | m = text.match( /^\s*\[(.*?)\]/ ); 940 | 941 | if ( m ) { 942 | 943 | consumed += m[ 0 ].length; 944 | 945 | // [links][] uses links as its reference 946 | attrs = { ref: ( m[ 1 ] || String(children) ).toLowerCase(), original: orig.substr( 0, consumed ) }; 947 | 948 | link = [ "link_ref", attrs ].concat( children ); 949 | 950 | // We can't check if the reference is known here as it likely wont be 951 | // found till after. Check it in md tree->hmtl tree conversion. 952 | // Store the original so that conversion can revert if the ref isn't found. 953 | return [ consumed, link ]; 954 | } 955 | 956 | // [id] 957 | // Only if id is plain (no formatting.) 958 | if ( children.length == 1 && typeof children[0] == "string" ) { 959 | 960 | attrs = { ref: children[0].toLowerCase(), original: orig.substr( 0, consumed ) }; 961 | link = [ "link_ref", attrs, children[0] ]; 962 | return [ consumed, link ]; 963 | } 964 | 965 | // Just consume the "[" 966 | return [ 1, "[" ]; 967 | }, 968 | 969 | 970 | "<": function autoLink( text ) { 971 | var m; 972 | 973 | if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) { 974 | if ( m[3] ) { 975 | return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ]; 976 | 977 | } 978 | else if ( m[2] == "mailto" ) { 979 | return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ]; 980 | } 981 | else 982 | return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ]; 983 | } 984 | 985 | return [ 1, "<" ]; 986 | }, 987 | 988 | "`": function inlineCode( text ) { 989 | // Inline code block. as many backticks as you like to start it 990 | // Always skip over the opening ticks. 991 | var m = text.match( /(`+)(([\s\S]*?)\1)/ ); 992 | 993 | if ( m && m[2] ) 994 | return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ]; 995 | else { 996 | // TODO: No matching end code found - warn! 997 | return [ 1, "`" ]; 998 | } 999 | }, 1000 | 1001 | " \n": function lineBreak( text ) { 1002 | return [ 3, [ "linebreak" ] ]; 1003 | } 1004 | 1005 | }; 1006 | 1007 | // Meta Helper/generator method for em and strong handling 1008 | function strong_em( tag, md ) { 1009 | 1010 | var state_slot = tag + "_state", 1011 | other_slot = tag == "strong" ? "em_state" : "strong_state"; 1012 | 1013 | function CloseTag(len) { 1014 | this.len_after = len; 1015 | this.name = "close_" + md; 1016 | } 1017 | 1018 | return function ( text, orig_match ) { 1019 | 1020 | if ( this[state_slot][0] == md ) { 1021 | // Most recent em is of this type 1022 | //D:this.debug("closing", md); 1023 | this[state_slot].shift(); 1024 | 1025 | // "Consume" everything to go back to the recrusion in the else-block below 1026 | return[ text.length, new CloseTag(text.length-md.length) ]; 1027 | } 1028 | else { 1029 | // Store a clone of the em/strong states 1030 | var other = this[other_slot].slice(), 1031 | state = this[state_slot].slice(); 1032 | 1033 | this[state_slot].unshift(md); 1034 | 1035 | //D:this.debug_indent += " "; 1036 | 1037 | // Recurse 1038 | var res = this.processInline( text.substr( md.length ) ); 1039 | //D:this.debug_indent = this.debug_indent.substr(2); 1040 | 1041 | var last = res[res.length - 1]; 1042 | 1043 | //D:this.debug("processInline from", tag + ": ", uneval( res ) ); 1044 | 1045 | var check = this[state_slot].shift(); 1046 | if ( last instanceof CloseTag ) { 1047 | res.pop(); 1048 | // We matched! Huzzah. 1049 | var consumed = text.length - last.len_after; 1050 | return [ consumed, [ tag ].concat(res) ]; 1051 | } 1052 | else { 1053 | // Restore the state of the other kind. We might have mistakenly closed it. 1054 | this[other_slot] = other; 1055 | this[state_slot] = state; 1056 | 1057 | // We can't reuse the processed result as it could have wrong parsing contexts in it. 1058 | return [ md.length, md ]; 1059 | } 1060 | } 1061 | }; // End returned function 1062 | } 1063 | 1064 | Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**"); 1065 | Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__"); 1066 | Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*"); 1067 | Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_"); 1068 | 1069 | 1070 | // Build default order from insertion order. 1071 | Markdown.buildBlockOrder = function(d) { 1072 | var ord = []; 1073 | for ( var i in d ) { 1074 | if ( i == "__order__" || i == "__call__" ) continue; 1075 | ord.push( i ); 1076 | } 1077 | d.__order__ = ord; 1078 | }; 1079 | 1080 | // Build patterns for inline matcher 1081 | Markdown.buildInlinePatterns = function(d) { 1082 | var patterns = []; 1083 | 1084 | for ( var i in d ) { 1085 | // __foo__ is reserved and not a pattern 1086 | if ( i.match( /^__.*__$/) ) continue; 1087 | var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) 1088 | .replace( /\n/, "\\n" ); 1089 | patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); 1090 | } 1091 | 1092 | patterns = patterns.join("|"); 1093 | d.__patterns__ = patterns; 1094 | //print("patterns:", uneval( patterns ) ); 1095 | 1096 | var fn = d.__call__; 1097 | d.__call__ = function(text, pattern) { 1098 | if ( pattern != undefined ) { 1099 | return fn.call(this, text, pattern); 1100 | } 1101 | else 1102 | { 1103 | return fn.call(this, text, patterns); 1104 | } 1105 | }; 1106 | }; 1107 | 1108 | Markdown.DialectHelpers = {}; 1109 | Markdown.DialectHelpers.inline_until_char = function( text, want ) { 1110 | var consumed = 0, 1111 | nodes = []; 1112 | 1113 | while ( true ) { 1114 | if ( text.charAt( consumed ) == want ) { 1115 | // Found the character we were looking for 1116 | consumed++; 1117 | return [ consumed, nodes ]; 1118 | } 1119 | 1120 | if ( consumed >= text.length ) { 1121 | // No closing char found. Abort. 1122 | return null; 1123 | } 1124 | 1125 | var res = this.dialect.inline.__oneElement__.call(this, text.substr( consumed ) ); 1126 | consumed += res[ 0 ]; 1127 | // Add any returned nodes. 1128 | nodes.push.apply( nodes, res.slice( 1 ) ); 1129 | } 1130 | } 1131 | 1132 | // Helper function to make sub-classing a dialect easier 1133 | Markdown.subclassDialect = function( d ) { 1134 | function Block() {} 1135 | Block.prototype = d.block; 1136 | function Inline() {} 1137 | Inline.prototype = d.inline; 1138 | 1139 | return { block: new Block(), inline: new Inline() }; 1140 | }; 1141 | 1142 | Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block ); 1143 | Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline ); 1144 | 1145 | Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber ); 1146 | 1147 | Markdown.dialects.Maruku.processMetaHash = function processMetaHash( meta_string ) { 1148 | var meta = split_meta_hash( meta_string ), 1149 | attr = {}; 1150 | 1151 | for ( var i = 0; i < meta.length; ++i ) { 1152 | // id: #foo 1153 | if ( /^#/.test( meta[ i ] ) ) { 1154 | attr.id = meta[ i ].substring( 1 ); 1155 | } 1156 | // class: .foo 1157 | else if ( /^\./.test( meta[ i ] ) ) { 1158 | // if class already exists, append the new one 1159 | if ( attr["class"] ) { 1160 | attr["class"] = attr["class"] + meta[ i ].replace( /./, " " ); 1161 | } 1162 | else { 1163 | attr["class"] = meta[ i ].substring( 1 ); 1164 | } 1165 | } 1166 | // attribute: foo=bar 1167 | else if ( /\=/.test( meta[ i ] ) ) { 1168 | var s = meta[ i ].split( /\=/ ); 1169 | attr[ s[ 0 ] ] = s[ 1 ]; 1170 | } 1171 | } 1172 | 1173 | return attr; 1174 | } 1175 | 1176 | function split_meta_hash( meta_string ) { 1177 | var meta = meta_string.split( "" ), 1178 | parts = [ "" ], 1179 | in_quotes = false; 1180 | 1181 | while ( meta.length ) { 1182 | var letter = meta.shift(); 1183 | switch ( letter ) { 1184 | case " " : 1185 | // if we're in a quoted section, keep it 1186 | if ( in_quotes ) { 1187 | parts[ parts.length - 1 ] += letter; 1188 | } 1189 | // otherwise make a new part 1190 | else { 1191 | parts.push( "" ); 1192 | } 1193 | break; 1194 | case "'" : 1195 | case '"' : 1196 | // reverse the quotes and move straight on 1197 | in_quotes = !in_quotes; 1198 | break; 1199 | case "\\" : 1200 | // shift off the next letter to be used straight away. 1201 | // it was escaped so we'll keep it whatever it is 1202 | letter = meta.shift(); 1203 | default : 1204 | parts[ parts.length - 1 ] += letter; 1205 | break; 1206 | } 1207 | } 1208 | 1209 | return parts; 1210 | } 1211 | 1212 | Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) { 1213 | // we're only interested in the first block 1214 | if ( block.lineNumber > 1 ) return undefined; 1215 | 1216 | // document_meta blocks consist of one or more lines of `Key: Value\n` 1217 | if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined; 1218 | 1219 | // make an attribute node if it doesn't exist 1220 | if ( !extract_attr( this.tree ) ) { 1221 | this.tree.splice( 1, 0, {} ); 1222 | } 1223 | 1224 | var pairs = block.split( /\n/ ); 1225 | for ( p in pairs ) { 1226 | var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ), 1227 | key = m[ 1 ].toLowerCase(), 1228 | value = m[ 2 ]; 1229 | 1230 | this.tree[ 1 ][ key ] = value; 1231 | } 1232 | 1233 | // document_meta produces no content! 1234 | return []; 1235 | }; 1236 | 1237 | Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) { 1238 | // check if the last line of the block is an meta hash 1239 | var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ ); 1240 | if ( !m ) return undefined; 1241 | 1242 | // process the meta hash 1243 | var attr = this.dialect.processMetaHash( m[ 2 ] ); 1244 | 1245 | var hash; 1246 | 1247 | // if we matched ^ then we need to apply meta to the previous block 1248 | if ( m[ 1 ] === "" ) { 1249 | var node = this.tree[ this.tree.length - 1 ]; 1250 | hash = extract_attr( node ); 1251 | 1252 | // if the node is a string (rather than JsonML), bail 1253 | if ( typeof node === "string" ) return undefined; 1254 | 1255 | // create the attribute hash if it doesn't exist 1256 | if ( !hash ) { 1257 | hash = {}; 1258 | node.splice( 1, 0, hash ); 1259 | } 1260 | 1261 | // add the attributes in 1262 | for ( a in attr ) { 1263 | hash[ a ] = attr[ a ]; 1264 | } 1265 | 1266 | // return nothing so the meta hash is removed 1267 | return []; 1268 | } 1269 | 1270 | // pull the meta hash off the block and process what's left 1271 | var b = block.replace( /\n.*$/, "" ), 1272 | result = this.processBlock( b, [] ); 1273 | 1274 | // get or make the attributes hash 1275 | hash = extract_attr( result[ 0 ] ); 1276 | if ( !hash ) { 1277 | hash = {}; 1278 | result[ 0 ].splice( 1, 0, hash ); 1279 | } 1280 | 1281 | // attach the attributes to the block 1282 | for ( a in attr ) { 1283 | hash[ a ] = attr[ a ]; 1284 | } 1285 | 1286 | return result; 1287 | }; 1288 | 1289 | Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) { 1290 | // one or more terms followed by one or more definitions, in a single block 1291 | var tight = /^((?:[^\s:].*\n)+):\s+([\s\S]+)$/, 1292 | list = [ "dl" ], 1293 | i, m; 1294 | 1295 | // see if we're dealing with a tight or loose block 1296 | if ( ( m = block.match( tight ) ) ) { 1297 | // pull subsequent tight DL blocks out of `next` 1298 | var blocks = [ block ]; 1299 | while ( next.length && tight.exec( next[ 0 ] ) ) { 1300 | blocks.push( next.shift() ); 1301 | } 1302 | 1303 | for ( var b = 0; b < blocks.length; ++b ) { 1304 | var m = blocks[ b ].match( tight ), 1305 | terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ), 1306 | defns = m[ 2 ].split( /\n:\s+/ ); 1307 | 1308 | // print( uneval( m ) ); 1309 | 1310 | for ( i = 0; i < terms.length; ++i ) { 1311 | list.push( [ "dt", terms[ i ] ] ); 1312 | } 1313 | 1314 | for ( i = 0; i < defns.length; ++i ) { 1315 | // run inline processing over the definition 1316 | list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) ); 1317 | } 1318 | } 1319 | } 1320 | else { 1321 | return undefined; 1322 | } 1323 | 1324 | return [ list ]; 1325 | }; 1326 | 1327 | // splits on unescaped instances of @ch. If @ch is not a character the result 1328 | // can be unpredictable 1329 | 1330 | Markdown.dialects.Maruku.block.table = function table (block, next) { 1331 | 1332 | var _split_on_unescaped = function(s, ch) { 1333 | ch = ch || '\\s'; 1334 | if (ch.match(/^[\\|\[\]{}?*.+^$]$/)) { ch = '\\' + ch; } 1335 | var res = [ ], 1336 | r = new RegExp('^((?:\\\\.|[^\\\\' + ch + '])*)' + ch + '(.*)'), 1337 | m; 1338 | while(m = s.match(r)) { 1339 | res.push(m[1]); 1340 | s = m[2]; 1341 | } 1342 | res.push(s); 1343 | return res; 1344 | } 1345 | 1346 | var leading_pipe = /^ {0,3}\|(.+)\n {0,3}\|\s*([\-:]+[\-| :]*)\n((?:\s*\|.*(?:\n|$))*)(?=\n|$)/, 1347 | // find at least an unescaped pipe in each line 1348 | no_leading_pipe = /^ {0,3}(\S(?:\\.|[^\\|])*\|.*)\n {0,3}([\-:]+\s*\|[\-| :]*)\n((?:(?:\\.|[^\\|])*\|.*(?:\n|$))*)(?=\n|$)/, 1349 | i, m; 1350 | if (m = block.match(leading_pipe)) { 1351 | // remove leading pipes in contents 1352 | // (header and horizontal rule already have the leading pipe left out) 1353 | m[3] = m[3].replace(/^\s*\|/gm, ''); 1354 | } else if (! ( m = block.match(no_leading_pipe))) { 1355 | return undefined; 1356 | } 1357 | 1358 | var table = [ "table", [ "thead", [ "tr" ] ], [ "tbody" ] ]; 1359 | 1360 | // remove trailing pipes, then split on pipes 1361 | // (no escaped pipes are allowed in horizontal rule) 1362 | m[2] = m[2].replace(/\|\s*$/, '').split('|'); 1363 | 1364 | // process alignment 1365 | var html_attrs = [ ]; 1366 | forEach (m[2], function (s) { 1367 | if (s.match(/^\s*-+:\s*$/)) html_attrs.push({align: "right"}); 1368 | else if (s.match(/^\s*:-+\s*$/)) html_attrs.push({align: "left"}); 1369 | else if (s.match(/^\s*:-+:\s*$/)) html_attrs.push({align: "center"}); 1370 | else html_attrs.push({}); 1371 | }); 1372 | 1373 | // now for the header, avoid escaped pipes 1374 | m[1] = _split_on_unescaped(m[1].replace(/\|\s*$/, ''), '|'); 1375 | for (i = 0; i < m[1].length; i++) { 1376 | table[1][1].push(['th', html_attrs[i] || {}].concat( 1377 | this.processInline(m[1][i].trim()))); 1378 | } 1379 | 1380 | // now for body contents 1381 | forEach (m[3].replace(/\|\s*$/mg, '').split('\n'), function (row) { 1382 | var html_row = ['tr']; 1383 | row = _split_on_unescaped(row, '|'); 1384 | for (i = 0; i < row.length; i++) { 1385 | html_row.push(['td', html_attrs[i] || {}].concat(this.processInline(row[i].trim()))); 1386 | } 1387 | table[2].push(html_row); 1388 | }, this); 1389 | 1390 | return [table]; 1391 | } 1392 | 1393 | Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) { 1394 | if ( !out.length ) { 1395 | return [ 2, "{:" ]; 1396 | } 1397 | 1398 | // get the preceeding element 1399 | var before = out[ out.length - 1 ]; 1400 | 1401 | if ( typeof before === "string" ) { 1402 | return [ 2, "{:" ]; 1403 | } 1404 | 1405 | // match a meta hash 1406 | var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ ); 1407 | 1408 | // no match, false alarm 1409 | if ( !m ) { 1410 | return [ 2, "{:" ]; 1411 | } 1412 | 1413 | // attach the attributes to the preceeding element 1414 | var meta = this.dialect.processMetaHash( m[ 1 ] ), 1415 | attr = extract_attr( before ); 1416 | 1417 | if ( !attr ) { 1418 | attr = {}; 1419 | before.splice( 1, 0, attr ); 1420 | } 1421 | 1422 | for ( var k in meta ) { 1423 | attr[ k ] = meta[ k ]; 1424 | } 1425 | 1426 | // cut out the string and replace it with nothing 1427 | return [ m[ 0 ].length, "" ]; 1428 | }; 1429 | 1430 | Markdown.dialects.Maruku.inline.__escape__ = /^\\[\\`\*_{}\[\]()#\+.!\-|:]/; 1431 | 1432 | Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block ); 1433 | Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline ); 1434 | 1435 | var isArray = Array.isArray || function(obj) { 1436 | return Object.prototype.toString.call(obj) == "[object Array]"; 1437 | }; 1438 | 1439 | var forEach; 1440 | // Don't mess with Array.prototype. Its not friendly 1441 | if ( Array.prototype.forEach ) { 1442 | forEach = function( arr, cb, thisp ) { 1443 | return arr.forEach( cb, thisp ); 1444 | }; 1445 | } 1446 | else { 1447 | forEach = function(arr, cb, thisp) { 1448 | for (var i = 0; i < arr.length; i++) { 1449 | cb.call(thisp || arr, arr[i], i, arr); 1450 | } 1451 | } 1452 | } 1453 | 1454 | var isEmpty = function( obj ) { 1455 | for ( var key in obj ) { 1456 | if ( hasOwnProperty.call( obj, key ) ) { 1457 | return false; 1458 | } 1459 | } 1460 | 1461 | return true; 1462 | } 1463 | 1464 | function extract_attr( jsonml ) { 1465 | return isArray(jsonml) 1466 | && jsonml.length > 1 1467 | && typeof jsonml[ 1 ] === "object" 1468 | && !( isArray(jsonml[ 1 ]) ) 1469 | ? jsonml[ 1 ] 1470 | : undefined; 1471 | } 1472 | 1473 | 1474 | 1475 | /** 1476 | * renderJsonML( jsonml[, options] ) -> String 1477 | * - jsonml (Array): JsonML array to render to XML 1478 | * - options (Object): options 1479 | * 1480 | * Converts the given JsonML into well-formed XML. 1481 | * 1482 | * The options currently understood are: 1483 | * 1484 | * - root (Boolean): wether or not the root node should be included in the 1485 | * output, or just its children. The default `false` is to not include the 1486 | * root itself. 1487 | */ 1488 | expose.renderJsonML = function( jsonml, options ) { 1489 | options = options || {}; 1490 | // include the root element in the rendered output? 1491 | options.root = options.root || false; 1492 | 1493 | var content = []; 1494 | 1495 | if ( options.root ) { 1496 | content.push( render_tree( jsonml ) ); 1497 | } 1498 | else { 1499 | jsonml.shift(); // get rid of the tag 1500 | if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { 1501 | jsonml.shift(); // get rid of the attributes 1502 | } 1503 | 1504 | while ( jsonml.length ) { 1505 | content.push( render_tree( jsonml.shift() ) ); 1506 | } 1507 | } 1508 | 1509 | return content; 1510 | }; 1511 | 1512 | function escapeHTML( text ) { 1513 | return text.replace( /&/g, "&" ) 1514 | .replace( //g, ">" ) 1516 | .replace( /"/g, """ ) 1517 | .replace( /'/g, "'" ); 1518 | } 1519 | 1520 | function render_tree( jsonml ) { 1521 | // basic case 1522 | if ( typeof jsonml === "string" ) { 1523 | return ReactHelpers.htmlReactLinks('text', null, escapeHTML(jsonml)); // Here is the magic 1524 | } 1525 | 1526 | var tag = jsonml.shift(), 1527 | attributes = {}, 1528 | content = []; 1529 | 1530 | if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { 1531 | attributes = jsonml.shift(); 1532 | } 1533 | 1534 | while ( jsonml.length ) { 1535 | content.push( render_tree( jsonml.shift() ) ); 1536 | } 1537 | 1538 | var tag_attrs = ""; 1539 | for ( var a in attributes ) { 1540 | tag_attrs += " " + a + '="' + escapeHTML( attributes[ a ] ) + '"'; 1541 | } 1542 | return ReactHelpers.htmlReactLinks(tag, tag_attrs, content); // Here is the magic 1543 | } 1544 | 1545 | function convert_tree_to_html( tree, references, options ) { 1546 | var i; 1547 | options = options || {}; 1548 | 1549 | // shallow clone 1550 | var jsonml = tree.slice( 0 ); 1551 | 1552 | if ( typeof options.preprocessTreeNode === "function" ) { 1553 | jsonml = options.preprocessTreeNode(jsonml, references); 1554 | } 1555 | 1556 | // Clone attributes if they exist 1557 | var attrs = extract_attr( jsonml ); 1558 | if ( attrs ) { 1559 | jsonml[ 1 ] = {}; 1560 | for ( i in attrs ) { 1561 | jsonml[ 1 ][ i ] = attrs[ i ]; 1562 | } 1563 | attrs = jsonml[ 1 ]; 1564 | } 1565 | 1566 | // basic case 1567 | if ( typeof jsonml === "string" ) { 1568 | return jsonml; 1569 | } 1570 | 1571 | // convert this node 1572 | switch ( jsonml[ 0 ] ) { 1573 | case "header": 1574 | jsonml[ 0 ] = "h" + jsonml[ 1 ].level; 1575 | delete jsonml[ 1 ].level; 1576 | break; 1577 | case "bulletlist": 1578 | jsonml[ 0 ] = "ul"; 1579 | break; 1580 | case "numberlist": 1581 | jsonml[ 0 ] = "ol"; 1582 | break; 1583 | case "listitem": 1584 | jsonml[ 0 ] = "li"; 1585 | break; 1586 | case "para": 1587 | jsonml[ 0 ] = "p"; 1588 | break; 1589 | case "markdown": 1590 | jsonml[ 0 ] = "html"; 1591 | if ( attrs ) delete attrs.references; 1592 | break; 1593 | case "code_block": 1594 | jsonml[ 0 ] = "pre"; 1595 | i = attrs ? 2 : 1; 1596 | var code = [ "code" ]; 1597 | code.push.apply( code, jsonml.splice( i, jsonml.length - i ) ); 1598 | jsonml[ i ] = code; 1599 | break; 1600 | case "inlinecode": 1601 | jsonml[ 0 ] = "code"; 1602 | break; 1603 | case "img": 1604 | jsonml[ 1 ].src = jsonml[ 1 ].href; 1605 | delete jsonml[ 1 ].href; 1606 | break; 1607 | case "linebreak": 1608 | jsonml[ 0 ] = "br"; 1609 | break; 1610 | case "link": 1611 | jsonml[ 0 ] = "a"; 1612 | break; 1613 | case "link_ref": 1614 | jsonml[ 0 ] = "a"; 1615 | 1616 | // grab this ref and clean up the attribute node 1617 | var ref = references[ attrs.ref ]; 1618 | 1619 | // if the reference exists, make the link 1620 | if ( ref ) { 1621 | delete attrs.ref; 1622 | 1623 | // add in the href and title, if present 1624 | attrs.href = ref.href; 1625 | if ( ref.title ) { 1626 | attrs.title = ref.title; 1627 | } 1628 | 1629 | // get rid of the unneeded original text 1630 | delete attrs.original; 1631 | } 1632 | // the reference doesn't exist, so revert to plain text 1633 | else { 1634 | return attrs.original; 1635 | } 1636 | break; 1637 | case "img_ref": 1638 | jsonml[ 0 ] = "img"; 1639 | 1640 | // grab this ref and clean up the attribute node 1641 | var ref = references[ attrs.ref ]; 1642 | 1643 | // if the reference exists, make the link 1644 | if ( ref ) { 1645 | delete attrs.ref; 1646 | 1647 | // add in the href and title, if present 1648 | attrs.src = ref.href; 1649 | if ( ref.title ) { 1650 | attrs.title = ref.title; 1651 | } 1652 | 1653 | // get rid of the unneeded original text 1654 | delete attrs.original; 1655 | } 1656 | // the reference doesn't exist, so revert to plain text 1657 | else { 1658 | return attrs.original; 1659 | } 1660 | break; 1661 | } 1662 | 1663 | // convert all the children 1664 | i = 1; 1665 | 1666 | // deal with the attribute node, if it exists 1667 | if ( attrs ) { 1668 | // if there are keys, skip over it 1669 | for ( var key in jsonml[ 1 ] ) { 1670 | i = 2; 1671 | break; 1672 | } 1673 | // if there aren't, remove it 1674 | if ( i === 1 ) { 1675 | jsonml.splice( i, 1 ); 1676 | } 1677 | } 1678 | 1679 | for ( ; i < jsonml.length; ++i ) { 1680 | jsonml[ i ] = convert_tree_to_html( jsonml[ i ], references, options ); 1681 | } 1682 | 1683 | return jsonml; 1684 | } 1685 | 1686 | 1687 | // merges adjacent text nodes into a single node 1688 | function merge_text_nodes( jsonml ) { 1689 | // skip the tag name and attribute hash 1690 | var i = extract_attr( jsonml ) ? 2 : 1; 1691 | 1692 | while ( i < jsonml.length ) { 1693 | // if it's a string check the next item too 1694 | if ( typeof jsonml[ i ] === "string" ) { 1695 | if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) { 1696 | // merge the second string into the first and remove it 1697 | jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ]; 1698 | } 1699 | else { 1700 | ++i; 1701 | } 1702 | } 1703 | // if it's not a string recurse 1704 | else { 1705 | merge_text_nodes( jsonml[ i ] ); 1706 | ++i; 1707 | } 1708 | } 1709 | } 1710 | 1711 | } )( (function() { 1712 | return exports; 1713 | } )() ); 1714 | -------------------------------------------------------------------------------- /lib/react_helpers.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Linking, 4 | Text, 5 | View, 6 | } from 'react-native'; 7 | import _ from 'lodash'; 8 | import { mdStyle } from '../Markdown'; 9 | 10 | exports.htmlReactLinks = (tag, tag_attrs, content) => { 11 | if (tag === 'text') { 12 | return ( 13 | 14 | {content} 15 | 16 | ); 17 | } 18 | switch (true) { 19 | case /^h[123456]?$/.test(tag): 20 | case tag === 'strong': 21 | case tag === 'em': 22 | case tag === 'p': 23 | case tag === 'html': 24 | case tag === 'code': 25 | return ( 26 | 27 | {content} 28 | 29 | ); 30 | case tag === 'a': 31 | console.log(/href="(.*)"/.exec(tag_attrs)[1]); 32 | return ( 33 | { 34 | Linking.openURL(/href="(.*)"/.exec(tag_attrs)[1]); 35 | }}> 36 | {content} 37 | 38 | ); 39 | case tag === 'pre': 40 | return ( 41 | 42 | 43 | {content} 44 | 45 | 46 | ); 47 | case tag === 'blockquote': 48 | return ( 49 | 50 | 51 | 52 | {content} 53 | 54 | 55 | ); 56 | case tag === 'ul': 57 | case tag === 'ol': 58 | return ( 59 | 60 | {content} 61 | 62 | ); 63 | case /^[0-9]+li$/.test(tag): 64 | return ( 65 | 66 | 67 | {/^[0-9]+/.exec(tag)[0]}.{' '} 68 | 69 | 70 | {content} 71 | 72 | 73 | ); 74 | case tag === 'li': 75 | return ( 76 | 77 | 78 | 79 | {content} 80 | 81 | 82 | ); 83 | case tag === 'br': 84 | return ( 85 | 86 | {content} 87 | {'\n'} 88 | 89 | ); 90 | default: 91 | return ( 92 | 93 | {content} 94 | 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-markdown-syntax", 3 | "version": "0.1.0", 4 | "description": "A markdown component for react-native", 5 | "keywords": [ 6 | "markdown", 7 | "text processing", 8 | "ast", 9 | "react native" 10 | ], 11 | "maintainers": [ 12 | "Pierre Monge " 13 | ], 14 | "contributors": [ 15 | "Dominic Baggott (http://evilstreak.co.uk)", 16 | "Ash Berlin (http://ashberlin.com)", 17 | "XhmikosR ", 18 | "Robin Ward (http://eviltrout.com)", 19 | "Pierre Monge " 20 | ], 21 | "bugs": { 22 | "url": "https://github.com/Liroo/react-native-markdown-syntax/issues" 23 | }, 24 | "license": "MIT", 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/Liroo/react-native-markdown-syntax.git" 28 | }, 29 | "main": "./Markdown.js", 30 | "files": [ 31 | "lib", 32 | "README.md" 33 | ], 34 | "dependencies": { 35 | "lodash": "^4.16.4" 36 | }, 37 | "scripts": { 38 | "test": "echo \"No tests specified\" && exit 1" 39 | } 40 | } 41 | --------------------------------------------------------------------------------