react-native-enriched-html 0.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (747) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +402 -0
  3. package/ReactNativeEnrichedHtml.podspec +31 -0
  4. package/android/build.gradle +106 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +203 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +74 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerDelegate.java +70 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerInterface.java +31 -0
  9. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  10. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  11. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +456 -0
  12. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
  13. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +272 -0
  14. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +1595 -0
  15. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  16. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  17. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.cpp +16 -0
  18. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.h +20 -0
  19. package/android/gradle.properties +5 -0
  20. package/android/lint.gradle +70 -0
  21. package/android/src/main/AndroidManifest.xml +3 -0
  22. package/android/src/main/AndroidManifestNew.xml +2 -0
  23. package/android/src/main/java/com/swmansion/enriched/ReactNativeEnrichedPackage.kt +22 -0
  24. package/android/src/main/java/com/swmansion/enriched/common/AllowFontScaling.kt +36 -0
  25. package/android/src/main/java/com/swmansion/enriched/common/AsyncDrawable.kt +126 -0
  26. package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
  27. package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +16 -0
  28. package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
  29. package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
  30. package/android/src/main/java/com/swmansion/enriched/common/ForceRedrawSpan.kt +14 -0
  31. package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
  32. package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +9 -0
  33. package/android/src/main/java/com/swmansion/enriched/common/ResourceManager.kt +26 -0
  34. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +1057 -0
  35. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +83 -0
  36. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedAlignmentSpan.kt +19 -0
  37. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
  38. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
  39. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +92 -0
  40. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCodeBlockSpan.kt +81 -0
  41. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
  42. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
  43. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
  44. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
  45. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
  46. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
  47. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedImageSpan.kt +185 -0
  48. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
  49. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
  50. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +29 -0
  51. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
  52. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt +79 -0
  53. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
  54. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
  55. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +62 -0
  56. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
  57. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
  58. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
  59. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
  60. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
  61. package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +4 -0
  62. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextMovementMethod.kt +75 -0
  63. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +84 -0
  64. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt +202 -0
  65. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +362 -0
  66. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextViewManager.kt +157 -0
  67. package/android/src/main/java/com/swmansion/enriched/text/MeasurementStore.kt +170 -0
  68. package/android/src/main/java/com/swmansion/enriched/text/events/OnLinkPressEvent.kt +23 -0
  69. package/android/src/main/java/com/swmansion/enriched/text/events/OnMentionPressEvent.kt +32 -0
  70. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextAlignmentSpan.kt +14 -0
  71. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBlockQuoteSpan.kt +14 -0
  72. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBoldSpan.kt +14 -0
  73. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCheckboxListSpan.kt +15 -0
  74. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCodeBlockSpan.kt +14 -0
  75. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH1Span.kt +14 -0
  76. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH2Span.kt +14 -0
  77. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH3Span.kt +15 -0
  78. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH4Span.kt +15 -0
  79. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH5Span.kt +14 -0
  80. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH6Span.kt +14 -0
  81. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextImageSpan.kt +57 -0
  82. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextInlineCodeSpan.kt +14 -0
  83. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextItalicSpan.kt +14 -0
  84. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextLinkSpan.kt +41 -0
  85. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextMentionSpan.kt +63 -0
  86. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextOrderedListSpan.kt +16 -0
  87. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextStrikeThroughSpan.kt +15 -0
  88. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnderlineSpan.kt +15 -0
  89. package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnorderedListSpan.kt +15 -0
  90. package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextClickableSpan.kt +9 -0
  91. package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextSpan.kt +10 -0
  92. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
  93. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +86 -0
  94. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1144 -0
  95. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt +27 -0
  96. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +500 -0
  97. package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +230 -0
  98. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +65 -0
  99. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
  100. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeSelectionEvent.kt +30 -0
  101. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
  102. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
  103. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
  104. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
  105. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
  106. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
  107. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
  108. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionDetectedEvent.kt +30 -0
  109. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +40 -0
  110. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
  111. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnRequestHtmlResultEvent.kt +32 -0
  112. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnSubmitEditingEvent.kt +29 -0
  113. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputAlignmentSpan.kt +14 -0
  114. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
  115. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
  116. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
  117. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
  118. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
  119. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
  120. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
  121. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
  122. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
  123. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
  124. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
  125. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
  126. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
  127. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +16 -0
  128. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
  129. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
  130. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
  131. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
  132. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
  133. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +45 -0
  134. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +240 -0
  135. package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
  136. package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +366 -0
  137. package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +371 -0
  138. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +244 -0
  139. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +261 -0
  140. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +440 -0
  141. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +435 -0
  142. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
  143. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +342 -0
  144. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +318 -0
  145. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +157 -0
  146. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +59 -0
  147. package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
  148. package/android/src/main/java/com/swmansion/enriched/textinput/utils/ShortcutsHandler.kt +150 -0
  149. package/android/src/main/java/com/swmansion/enriched/textinput/utils/StyleUtils.kt +51 -0
  150. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +107 -0
  151. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt +109 -0
  152. package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +106 -0
  153. package/android/src/main/new_arch/CMakeLists.txt +62 -0
  154. package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
  155. package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
  156. package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +16 -0
  157. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextComponentDescriptor.h +32 -0
  158. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h +35 -0
  159. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.cpp +53 -0
  160. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.h +25 -0
  161. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.cpp +35 -0
  162. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h +53 -0
  163. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.cpp +9 -0
  164. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.h +24 -0
  165. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextMeasurementManager.cpp +45 -0
  166. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextMeasurementManager.h +25 -0
  167. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.cpp +21 -0
  168. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.h +39 -0
  169. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +44 -0
  170. package/android/src/main/res/drawable/broken_image.xml +10 -0
  171. package/cpp/CMakeLists.txt +50 -0
  172. package/cpp/GumboParser/GumboParser.h +34043 -0
  173. package/cpp/README.md +59 -0
  174. package/cpp/parser/GumboNormalizer.c +973 -0
  175. package/cpp/parser/GumboParser.cpp +16 -0
  176. package/cpp/parser/GumboParser.hpp +23 -0
  177. package/cpp/tests/GumboParserTest.cpp +511 -0
  178. package/ios/EnrichedTextInputView.h +54 -0
  179. package/ios/EnrichedTextInputView.mm +2182 -0
  180. package/ios/EnrichedTextInputViewManager.mm +13 -0
  181. package/ios/EnrichedTextView.h +35 -0
  182. package/ios/EnrichedTextView.mm +806 -0
  183. package/ios/EnrichedTextViewManager.mm +13 -0
  184. package/ios/config/EnrichedConfig.h +114 -0
  185. package/ios/config/EnrichedConfig.mm +727 -0
  186. package/ios/enrichedInputTextView/EnrichedInputTextView.h +6 -0
  187. package/ios/enrichedInputTextView/EnrichedInputTextView.mm +334 -0
  188. package/ios/enrichedTextTextView/EnrichedTextTextView.h +16 -0
  189. package/ios/enrichedTextTextView/EnrichedTextTextView.mm +71 -0
  190. package/ios/extensions/ColorExtension.h +8 -0
  191. package/ios/extensions/ColorExtension.mm +48 -0
  192. package/ios/extensions/FontExtension.h +11 -0
  193. package/ios/extensions/FontExtension.mm +72 -0
  194. package/ios/extensions/ImageExtension.h +33 -0
  195. package/ios/extensions/ImageExtension.mm +176 -0
  196. package/ios/extensions/LayoutManagerExtension.h +6 -0
  197. package/ios/extensions/LayoutManagerExtension.mm +465 -0
  198. package/ios/extensions/StringExtension.h +15 -0
  199. package/ios/extensions/StringExtension.mm +144 -0
  200. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
  201. package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
  202. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +456 -0
  203. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
  204. package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +272 -0
  205. package/ios/generated/ReactNativeEnrichedSpec/Props.h +1595 -0
  206. package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +570 -0
  207. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
  208. package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
  209. package/ios/generated/ReactNativeEnrichedSpec/States.cpp +16 -0
  210. package/ios/generated/ReactNativeEnrichedSpec/States.h +20 -0
  211. package/ios/htmlParser/HtmlParser.h +14 -0
  212. package/ios/htmlParser/HtmlParser.mm +1486 -0
  213. package/ios/inputAttributesManager/InputAttributesManager.h +18 -0
  214. package/ios/inputAttributesManager/InputAttributesManager.mm +222 -0
  215. package/ios/inputHtmlParser/InputHtmlParser.h +10 -0
  216. package/ios/inputHtmlParser/InputHtmlParser.mm +228 -0
  217. package/ios/interfaces/AlignmentEntry.h +9 -0
  218. package/ios/interfaces/AlignmentEntry.mm +4 -0
  219. package/ios/interfaces/AttributeEntry.h +9 -0
  220. package/ios/interfaces/AttributeEntry.mm +4 -0
  221. package/ios/interfaces/BaseStyleProtocol.h +17 -0
  222. package/ios/interfaces/EnrichedTextStyleHeaders.h +62 -0
  223. package/ios/interfaces/EnrichedViewHost.h +26 -0
  224. package/ios/interfaces/ImageAttachment.h +11 -0
  225. package/ios/interfaces/ImageAttachment.mm +107 -0
  226. package/ios/interfaces/ImageData.h +10 -0
  227. package/ios/interfaces/ImageData.mm +4 -0
  228. package/ios/interfaces/LinkData.h +11 -0
  229. package/ios/interfaces/LinkData.mm +43 -0
  230. package/ios/interfaces/LinkRegexConfig.h +19 -0
  231. package/ios/interfaces/LinkRegexConfig.mm +37 -0
  232. package/ios/interfaces/MediaAttachment.h +23 -0
  233. package/ios/interfaces/MediaAttachment.mm +31 -0
  234. package/ios/interfaces/MentionParams.h +8 -0
  235. package/ios/interfaces/MentionParams.mm +4 -0
  236. package/ios/interfaces/MentionStyleProps.h +17 -0
  237. package/ios/interfaces/MentionStyleProps.mm +80 -0
  238. package/ios/interfaces/StyleBase.h +38 -0
  239. package/ios/interfaces/StyleBase.mm +279 -0
  240. package/ios/interfaces/StyleHeaders.h +114 -0
  241. package/ios/interfaces/StylePair.h +9 -0
  242. package/ios/interfaces/StylePair.mm +4 -0
  243. package/ios/interfaces/StyleTypeEnum.h +27 -0
  244. package/ios/interfaces/TextDecorationLineEnum.h +6 -0
  245. package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
  246. package/ios/internals/EnrichedTextComponentDescriptor.h +19 -0
  247. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +19 -0
  248. package/ios/internals/EnrichedTextInputViewShadowNode.h +44 -0
  249. package/ios/internals/EnrichedTextInputViewShadowNode.mm +103 -0
  250. package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
  251. package/ios/internals/EnrichedTextInputViewState.h +22 -0
  252. package/ios/internals/EnrichedTextViewShadowNode.h +34 -0
  253. package/ios/internals/EnrichedTextViewShadowNode.mm +72 -0
  254. package/ios/internals/EnrichedTextViewState.cpp +7 -0
  255. package/ios/internals/EnrichedTextViewState.h +18 -0
  256. package/ios/styles/AlignmentStyle.mm +203 -0
  257. package/ios/styles/BlockQuoteStyle.mm +54 -0
  258. package/ios/styles/BoldStyle.mm +37 -0
  259. package/ios/styles/CheckboxListStyle.mm +154 -0
  260. package/ios/styles/CodeBlockStyle.mm +49 -0
  261. package/ios/styles/EnrichedTextStyles.mm +61 -0
  262. package/ios/styles/H1Style.mm +20 -0
  263. package/ios/styles/H2Style.mm +20 -0
  264. package/ios/styles/H3Style.mm +20 -0
  265. package/ios/styles/H4Style.mm +20 -0
  266. package/ios/styles/H5Style.mm +20 -0
  267. package/ios/styles/H6Style.mm +20 -0
  268. package/ios/styles/HeadingStyleBase.mm +96 -0
  269. package/ios/styles/ImageStyle.mm +150 -0
  270. package/ios/styles/InlineCodeStyle.mm +65 -0
  271. package/ios/styles/ItalicStyle.mm +37 -0
  272. package/ios/styles/LinkStyle.mm +518 -0
  273. package/ios/styles/MentionStyle.mm +592 -0
  274. package/ios/styles/OrderedListStyle.mm +48 -0
  275. package/ios/styles/StrikethroughStyle.mm +25 -0
  276. package/ios/styles/UnderlineStyle.mm +24 -0
  277. package/ios/styles/UnorderedListStyle.mm +48 -0
  278. package/ios/textHtmlParser/TextHtmlParser.h +10 -0
  279. package/ios/textHtmlParser/TextHtmlParser.mm +182 -0
  280. package/ios/utils/AlignmentUtils.h +20 -0
  281. package/ios/utils/AlignmentUtils.mm +111 -0
  282. package/ios/utils/AttachmentLayoutUtils.h +24 -0
  283. package/ios/utils/AttachmentLayoutUtils.mm +144 -0
  284. package/ios/utils/CheckboxHitTestUtils.h +10 -0
  285. package/ios/utils/CheckboxHitTestUtils.mm +122 -0
  286. package/ios/utils/DotReplacementUtils.h +10 -0
  287. package/ios/utils/DotReplacementUtils.mm +68 -0
  288. package/ios/utils/EnrichedTextTouchHandler.h +14 -0
  289. package/ios/utils/EnrichedTextTouchHandler.mm +152 -0
  290. package/ios/utils/KeyboardUtils.h +7 -0
  291. package/ios/utils/KeyboardUtils.mm +30 -0
  292. package/ios/utils/OccurenceUtils.h +44 -0
  293. package/ios/utils/OccurenceUtils.mm +185 -0
  294. package/ios/utils/ParagraphAttributesUtils.h +18 -0
  295. package/ios/utils/ParagraphAttributesUtils.mm +296 -0
  296. package/ios/utils/RangeUtils.h +12 -0
  297. package/ios/utils/RangeUtils.mm +183 -0
  298. package/ios/utils/ShortcutsUtils.h +21 -0
  299. package/ios/utils/ShortcutsUtils.mm +486 -0
  300. package/ios/utils/StyleUtils.h +33 -0
  301. package/ios/utils/StyleUtils.mm +290 -0
  302. package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
  303. package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
  304. package/ios/utils/TextInsertionUtils.h +17 -0
  305. package/ios/utils/TextInsertionUtils.mm +60 -0
  306. package/ios/utils/TextListsUtils.h +40 -0
  307. package/ios/utils/TextListsUtils.mm +93 -0
  308. package/ios/utils/WeakBox.h +3 -0
  309. package/ios/utils/WeakBox.m +4 -0
  310. package/ios/utils/WordsUtils.h +7 -0
  311. package/ios/utils/WordsUtils.mm +98 -0
  312. package/ios/utils/ZeroWidthSpaceUtils.h +14 -0
  313. package/ios/utils/ZeroWidthSpaceUtils.mm +310 -0
  314. package/lib/module/index.js +5 -0
  315. package/lib/module/index.js.map +1 -0
  316. package/lib/module/index.native.js +7 -0
  317. package/lib/module/index.native.js.map +1 -0
  318. package/lib/module/native/EnrichedText.js +76 -0
  319. package/lib/module/native/EnrichedText.js.map +1 -0
  320. package/lib/module/native/EnrichedTextInput.js +306 -0
  321. package/lib/module/native/EnrichedTextInput.js.map +1 -0
  322. package/lib/module/package.json +1 -0
  323. package/lib/module/spec/EnrichedTextInputNativeComponent.ts +524 -0
  324. package/lib/module/spec/EnrichedTextNativeComponent.ts +110 -0
  325. package/lib/module/types.js +4 -0
  326. package/lib/module/types.js.map +1 -0
  327. package/lib/module/utils/EnrichedTextInputDefaultProps.js +20 -0
  328. package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
  329. package/lib/module/utils/defaultHtmlStyle.js +83 -0
  330. package/lib/module/utils/defaultHtmlStyle.js.map +1 -0
  331. package/lib/module/utils/expandMentionStylesForIndicators.js +14 -0
  332. package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -0
  333. package/lib/module/utils/isMentionStyleRecord.js +10 -0
  334. package/lib/module/utils/isMentionStyleRecord.js.map +1 -0
  335. package/lib/module/utils/normalizeHtmlStyle.js +126 -0
  336. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
  337. package/lib/module/utils/nullthrows.js +9 -0
  338. package/lib/module/utils/nullthrows.js.map +1 -0
  339. package/lib/module/utils/regexParser.js +46 -0
  340. package/lib/module/utils/regexParser.js.map +1 -0
  341. package/lib/module/web/EnrichedText.css +343 -0
  342. package/lib/module/web/EnrichedText.js +55 -0
  343. package/lib/module/web/EnrichedText.js.map +1 -0
  344. package/lib/module/web/EnrichedTextInput.js +316 -0
  345. package/lib/module/web/EnrichedTextInput.js.map +1 -0
  346. package/lib/module/web/adaptWebToNativeEvent.js +34 -0
  347. package/lib/module/web/adaptWebToNativeEvent.js.map +1 -0
  348. package/lib/module/web/constants/brokenImageGlyph.js +9 -0
  349. package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
  350. package/lib/module/web/constants/classNames.js +5 -0
  351. package/lib/module/web/constants/classNames.js.map +1 -0
  352. package/lib/module/web/emitLinkDetected.js +12 -0
  353. package/lib/module/web/emitLinkDetected.js.map +1 -0
  354. package/lib/module/web/formats/EnrichedBlockquote.js +32 -0
  355. package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -0
  356. package/lib/module/web/formats/EnrichedBold.js +22 -0
  357. package/lib/module/web/formats/EnrichedBold.js.map +1 -0
  358. package/lib/module/web/formats/EnrichedCheckboxItem.js +32 -0
  359. package/lib/module/web/formats/EnrichedCheckboxItem.js.map +1 -0
  360. package/lib/module/web/formats/EnrichedCheckboxList.js +35 -0
  361. package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -0
  362. package/lib/module/web/formats/EnrichedCode.js +15 -0
  363. package/lib/module/web/formats/EnrichedCode.js.map +1 -0
  364. package/lib/module/web/formats/EnrichedCodeBlock.js +43 -0
  365. package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -0
  366. package/lib/module/web/formats/EnrichedHeading.js +27 -0
  367. package/lib/module/web/formats/EnrichedHeading.js.map +1 -0
  368. package/lib/module/web/formats/EnrichedImage.js +57 -0
  369. package/lib/module/web/formats/EnrichedImage.js.map +1 -0
  370. package/lib/module/web/formats/EnrichedImageNodeView.js +90 -0
  371. package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -0
  372. package/lib/module/web/formats/EnrichedItalic.js +22 -0
  373. package/lib/module/web/formats/EnrichedItalic.js.map +1 -0
  374. package/lib/module/web/formats/EnrichedLink.js +180 -0
  375. package/lib/module/web/formats/EnrichedLink.js.map +1 -0
  376. package/lib/module/web/formats/EnrichedListItem.js +19 -0
  377. package/lib/module/web/formats/EnrichedListItem.js.map +1 -0
  378. package/lib/module/web/formats/EnrichedMention.js +50 -0
  379. package/lib/module/web/formats/EnrichedMention.js.map +1 -0
  380. package/lib/module/web/formats/EnrichedOrderedList.js +32 -0
  381. package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -0
  382. package/lib/module/web/formats/EnrichedStrike.js +22 -0
  383. package/lib/module/web/formats/EnrichedStrike.js.map +1 -0
  384. package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
  385. package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
  386. package/lib/module/web/formats/EnrichedUnderline.js +22 -0
  387. package/lib/module/web/formats/EnrichedUnderline.js.map +1 -0
  388. package/lib/module/web/formats/EnrichedUnorderedList.js +33 -0
  389. package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -0
  390. package/lib/module/web/formats/applyWrappingListToSelection.js +64 -0
  391. package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -0
  392. package/lib/module/web/formats/formatRules.js +84 -0
  393. package/lib/module/web/formats/formatRules.js.map +1 -0
  394. package/lib/module/web/formats/listKeyboard.js +53 -0
  395. package/lib/module/web/formats/listKeyboard.js.map +1 -0
  396. package/lib/module/web/formats/wrappedBlockKeyboard.js +58 -0
  397. package/lib/module/web/formats/wrappedBlockKeyboard.js.map +1 -0
  398. package/lib/module/web/normalization/checkboxHtmlNormalizer.js +54 -0
  399. package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
  400. package/lib/module/web/normalization/htmlNormalizer.js +514 -0
  401. package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
  402. package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
  403. package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
  404. package/lib/module/web/normalization/tiptapHtmlNormalizer.js +34 -0
  405. package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
  406. package/lib/module/web/pasteImages.js +78 -0
  407. package/lib/module/web/pasteImages.js.map +1 -0
  408. package/lib/module/web/pastedImageDimensions.js +49 -0
  409. package/lib/module/web/pastedImageDimensions.js.map +1 -0
  410. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
  411. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
  412. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
  413. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
  414. package/lib/module/web/pmPlugins/MentionPlugin/index.js +36 -0
  415. package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -0
  416. package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js +11 -0
  417. package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js.map +1 -0
  418. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +86 -0
  419. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -0
  420. package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js +5 -0
  421. package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js.map +1 -0
  422. package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js +56 -0
  423. package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js.map +1 -0
  424. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +57 -0
  425. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -0
  426. package/lib/module/web/pmPlugins/MentionPlugin/startMention.js +9 -0
  427. package/lib/module/web/pmPlugins/MentionPlugin/startMention.js.map +1 -0
  428. package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js +9 -0
  429. package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js.map +1 -0
  430. package/lib/module/web/pmPlugins/MentionPlugin/types.js +4 -0
  431. package/lib/module/web/pmPlugins/MentionPlugin/types.js.map +1 -0
  432. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
  433. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
  434. package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js +42 -0
  435. package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js.map +1 -0
  436. package/lib/module/web/pmPlugins/ShortcutPlugin.js +134 -0
  437. package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -0
  438. package/lib/module/web/pmPlugins/StrictMarksPlugin.js +100 -0
  439. package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -0
  440. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js +56 -0
  441. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -0
  442. package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js +28 -0
  443. package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js.map +1 -0
  444. package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js +29 -0
  445. package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js.map +1 -0
  446. package/lib/module/web/positionMapping.js +76 -0
  447. package/lib/module/web/positionMapping.js.map +1 -0
  448. package/lib/module/web/returnKeyTypeToEnterKeyHint.js +20 -0
  449. package/lib/module/web/returnKeyTypeToEnterKeyHint.js.map +1 -0
  450. package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
  451. package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
  452. package/lib/module/web/styleConversion/buildMentionRulesCSS.js +39 -0
  453. package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -0
  454. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
  455. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
  456. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +7 -0
  457. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -0
  458. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
  459. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
  460. package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js +18 -0
  461. package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
  462. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +168 -0
  463. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -0
  464. package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
  465. package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
  466. package/lib/module/web/styleConversion/mentionIndicatorCssKey.js +15 -0
  467. package/lib/module/web/styleConversion/mentionIndicatorCssKey.js.map +1 -0
  468. package/lib/module/web/styleConversion/toColor.js +18 -0
  469. package/lib/module/web/styleConversion/toColor.js.map +1 -0
  470. package/lib/module/web/useImageErrorFallback.js +33 -0
  471. package/lib/module/web/useImageErrorFallback.js.map +1 -0
  472. package/lib/module/web/useOnChangeHtml.js +8 -0
  473. package/lib/module/web/useOnChangeHtml.js.map +1 -0
  474. package/lib/module/web/useOnChangeState.js +91 -0
  475. package/lib/module/web/useOnChangeState.js.map +1 -0
  476. package/lib/module/web/useOnChangeText.js +11 -0
  477. package/lib/module/web/useOnChangeText.js.map +1 -0
  478. package/lib/module/web/useOnEditorChange.js +25 -0
  479. package/lib/module/web/useOnEditorChange.js.map +1 -0
  480. package/lib/module/web/useOnLinkDetected.js +55 -0
  481. package/lib/module/web/useOnLinkDetected.js.map +1 -0
  482. package/lib/module/web/usePressInteractions.js +19 -0
  483. package/lib/module/web/usePressInteractions.js.map +1 -0
  484. package/lib/module/web/useStableRef.js +13 -0
  485. package/lib/module/web/useStableRef.js.map +1 -0
  486. package/lib/typescript/package.json +1 -0
  487. package/lib/typescript/src/index.d.ts +4 -0
  488. package/lib/typescript/src/index.d.ts.map +1 -0
  489. package/lib/typescript/src/index.native.d.ts +5 -0
  490. package/lib/typescript/src/index.native.d.ts.map +1 -0
  491. package/lib/typescript/src/native/EnrichedText.d.ts +3 -0
  492. package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -0
  493. package/lib/typescript/src/native/EnrichedTextInput.d.ts +3 -0
  494. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -0
  495. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +405 -0
  496. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  497. package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts +88 -0
  498. package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts.map +1 -0
  499. package/lib/typescript/src/types.d.ts +721 -0
  500. package/lib/typescript/src/types.d.ts.map +1 -0
  501. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +13 -0
  502. package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
  503. package/lib/typescript/src/utils/defaultHtmlStyle.d.ts +4 -0
  504. package/lib/typescript/src/utils/defaultHtmlStyle.d.ts.map +1 -0
  505. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +3 -0
  506. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -0
  507. package/lib/typescript/src/utils/isMentionStyleRecord.d.ts +3 -0
  508. package/lib/typescript/src/utils/isMentionStyleRecord.d.ts.map +1 -0
  509. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +6 -0
  510. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
  511. package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
  512. package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
  513. package/lib/typescript/src/utils/regexParser.d.ts +3 -0
  514. package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
  515. package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
  516. package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
  517. package/lib/typescript/src/web/EnrichedTextInput.d.ts +4 -0
  518. package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -0
  519. package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts +3 -0
  520. package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts.map +1 -0
  521. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
  522. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
  523. package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
  524. package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
  525. package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
  526. package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
  527. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts +2 -0
  528. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -0
  529. package/lib/typescript/src/web/formats/EnrichedBold.d.ts +2 -0
  530. package/lib/typescript/src/web/formats/EnrichedBold.d.ts.map +1 -0
  531. package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts +2 -0
  532. package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts.map +1 -0
  533. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts +9 -0
  534. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -0
  535. package/lib/typescript/src/web/formats/EnrichedCode.d.ts +2 -0
  536. package/lib/typescript/src/web/formats/EnrichedCode.d.ts.map +1 -0
  537. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts +9 -0
  538. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -0
  539. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts +5 -0
  540. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -0
  541. package/lib/typescript/src/web/formats/EnrichedImage.d.ts +2 -0
  542. package/lib/typescript/src/web/formats/EnrichedImage.d.ts.map +1 -0
  543. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +3 -0
  544. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -0
  545. package/lib/typescript/src/web/formats/EnrichedItalic.d.ts +2 -0
  546. package/lib/typescript/src/web/formats/EnrichedItalic.d.ts.map +1 -0
  547. package/lib/typescript/src/web/formats/EnrichedLink.d.ts +8 -0
  548. package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -0
  549. package/lib/typescript/src/web/formats/EnrichedListItem.d.ts +2 -0
  550. package/lib/typescript/src/web/formats/EnrichedListItem.d.ts.map +1 -0
  551. package/lib/typescript/src/web/formats/EnrichedMention.d.ts +4 -0
  552. package/lib/typescript/src/web/formats/EnrichedMention.d.ts.map +1 -0
  553. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts +2 -0
  554. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -0
  555. package/lib/typescript/src/web/formats/EnrichedStrike.d.ts +2 -0
  556. package/lib/typescript/src/web/formats/EnrichedStrike.d.ts.map +1 -0
  557. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
  558. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
  559. package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts +2 -0
  560. package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts.map +1 -0
  561. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts +9 -0
  562. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -0
  563. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts +18 -0
  564. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -0
  565. package/lib/typescript/src/web/formats/formatRules.d.ts +12 -0
  566. package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -0
  567. package/lib/typescript/src/web/formats/listKeyboard.d.ts +4 -0
  568. package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -0
  569. package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts +12 -0
  570. package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts.map +1 -0
  571. package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts +3 -0
  572. package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
  573. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
  574. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
  575. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
  576. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
  577. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
  578. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
  579. package/lib/typescript/src/web/pasteImages.d.ts +10 -0
  580. package/lib/typescript/src/web/pasteImages.d.ts.map +1 -0
  581. package/lib/typescript/src/web/pastedImageDimensions.d.ts +9 -0
  582. package/lib/typescript/src/web/pastedImageDimensions.d.ts.map +1 -0
  583. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
  584. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
  585. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
  586. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
  587. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +9 -0
  588. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -0
  589. package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts +3 -0
  590. package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts.map +1 -0
  591. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts +4 -0
  592. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -0
  593. package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts +4 -0
  594. package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts.map +1 -0
  595. package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts +9 -0
  596. package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts.map +1 -0
  597. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts +3 -0
  598. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -0
  599. package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts +3 -0
  600. package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts.map +1 -0
  601. package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts +3 -0
  602. package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts.map +1 -0
  603. package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts +20 -0
  604. package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts.map +1 -0
  605. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
  606. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
  607. package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts +3 -0
  608. package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts.map +1 -0
  609. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts +7 -0
  610. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -0
  611. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts +3 -0
  612. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -0
  613. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +15 -0
  614. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -0
  615. package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts +3 -0
  616. package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts.map +1 -0
  617. package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts +3 -0
  618. package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts.map +1 -0
  619. package/lib/typescript/src/web/positionMapping.d.ts +39 -0
  620. package/lib/typescript/src/web/positionMapping.d.ts.map +1 -0
  621. package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts +4 -0
  622. package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts.map +1 -0
  623. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
  624. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
  625. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +3 -0
  626. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -0
  627. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
  628. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
  629. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +5 -0
  630. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -0
  631. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
  632. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
  633. package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts +9 -0
  634. package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
  635. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +11 -0
  636. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -0
  637. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
  638. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
  639. package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts +3 -0
  640. package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts.map +1 -0
  641. package/lib/typescript/src/web/styleConversion/toColor.d.ts +3 -0
  642. package/lib/typescript/src/web/styleConversion/toColor.d.ts.map +1 -0
  643. package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
  644. package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
  645. package/lib/typescript/src/web/useOnChangeHtml.d.ts +5 -0
  646. package/lib/typescript/src/web/useOnChangeHtml.d.ts.map +1 -0
  647. package/lib/typescript/src/web/useOnChangeState.d.ts +6 -0
  648. package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -0
  649. package/lib/typescript/src/web/useOnChangeText.d.ts +5 -0
  650. package/lib/typescript/src/web/useOnChangeText.d.ts.map +1 -0
  651. package/lib/typescript/src/web/useOnEditorChange.d.ts +6 -0
  652. package/lib/typescript/src/web/useOnEditorChange.d.ts.map +1 -0
  653. package/lib/typescript/src/web/useOnLinkDetected.d.ts +4 -0
  654. package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -0
  655. package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
  656. package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
  657. package/lib/typescript/src/web/useStableRef.d.ts +3 -0
  658. package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
  659. package/package.json +258 -1
  660. package/react-native.config.js +16 -0
  661. package/src/index.native.tsx +34 -0
  662. package/src/index.tsx +25 -0
  663. package/src/native/EnrichedText.tsx +115 -0
  664. package/src/native/EnrichedTextInput.tsx +374 -0
  665. package/src/spec/EnrichedTextInputNativeComponent.ts +524 -0
  666. package/src/spec/EnrichedTextNativeComponent.ts +110 -0
  667. package/src/types.ts +866 -0
  668. package/src/utils/EnrichedTextInputDefaultProps.ts +16 -0
  669. package/src/utils/defaultHtmlStyle.ts +83 -0
  670. package/src/utils/expandMentionStylesForIndicators.ts +19 -0
  671. package/src/utils/isMentionStyleRecord.ts +22 -0
  672. package/src/utils/normalizeHtmlStyle.ts +182 -0
  673. package/src/utils/nullthrows.ts +7 -0
  674. package/src/utils/regexParser.ts +56 -0
  675. package/src/web/EnrichedText.css +343 -0
  676. package/src/web/EnrichedText.tsx +82 -0
  677. package/src/web/EnrichedTextInput.tsx +417 -0
  678. package/src/web/adaptWebToNativeEvent.ts +37 -0
  679. package/src/web/constants/brokenImageGlyph.ts +7 -0
  680. package/src/web/constants/classNames.ts +2 -0
  681. package/src/web/emitLinkDetected.ts +29 -0
  682. package/src/web/formats/EnrichedBlockquote.ts +37 -0
  683. package/src/web/formats/EnrichedBold.ts +16 -0
  684. package/src/web/formats/EnrichedCheckboxItem.ts +35 -0
  685. package/src/web/formats/EnrichedCheckboxList.ts +50 -0
  686. package/src/web/formats/EnrichedCode.ts +13 -0
  687. package/src/web/formats/EnrichedCodeBlock.ts +54 -0
  688. package/src/web/formats/EnrichedHeading.ts +37 -0
  689. package/src/web/formats/EnrichedImage.ts +59 -0
  690. package/src/web/formats/EnrichedImageNodeView.tsx +87 -0
  691. package/src/web/formats/EnrichedItalic.ts +16 -0
  692. package/src/web/formats/EnrichedLink.ts +204 -0
  693. package/src/web/formats/EnrichedListItem.ts +17 -0
  694. package/src/web/formats/EnrichedMention.ts +55 -0
  695. package/src/web/formats/EnrichedOrderedList.ts +43 -0
  696. package/src/web/formats/EnrichedStrike.ts +16 -0
  697. package/src/web/formats/EnrichedTextAlign.ts +88 -0
  698. package/src/web/formats/EnrichedUnderline.ts +16 -0
  699. package/src/web/formats/EnrichedUnorderedList.ts +53 -0
  700. package/src/web/formats/applyWrappingListToSelection.ts +76 -0
  701. package/src/web/formats/formatRules.ts +128 -0
  702. package/src/web/formats/listKeyboard.ts +63 -0
  703. package/src/web/formats/wrappedBlockKeyboard.ts +76 -0
  704. package/src/web/normalization/checkboxHtmlNormalizer.ts +62 -0
  705. package/src/web/normalization/htmlNormalizer.ts +656 -0
  706. package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
  707. package/src/web/normalization/tiptapHtmlNormalizer.ts +43 -0
  708. package/src/web/pasteImages.ts +96 -0
  709. package/src/web/pastedImageDimensions.ts +40 -0
  710. package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
  711. package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
  712. package/src/web/pmPlugins/MentionPlugin/index.ts +45 -0
  713. package/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.ts +17 -0
  714. package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +102 -0
  715. package/src/web/pmPlugins/MentionPlugin/mentionPluginKey.ts +4 -0
  716. package/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.ts +68 -0
  717. package/src/web/pmPlugins/MentionPlugin/setMention.ts +73 -0
  718. package/src/web/pmPlugins/MentionPlugin/startMention.ts +14 -0
  719. package/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.ts +20 -0
  720. package/src/web/pmPlugins/MentionPlugin/types.ts +22 -0
  721. package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
  722. package/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.ts +57 -0
  723. package/src/web/pmPlugins/ShortcutPlugin.ts +105 -0
  724. package/src/web/pmPlugins/StrictMarksPlugin.ts +134 -0
  725. package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +84 -0
  726. package/src/web/pmPlugins/StripMarksInCodeBlockPlugin.ts +34 -0
  727. package/src/web/pmPlugins/StripMarksOnImagePlugin.ts +33 -0
  728. package/src/web/positionMapping.ts +81 -0
  729. package/src/web/returnKeyTypeToEnterKeyHint.ts +29 -0
  730. package/src/web/sanitization/htmlSanitizer.ts +8 -0
  731. package/src/web/styleConversion/buildMentionRulesCSS.ts +61 -0
  732. package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
  733. package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +13 -0
  734. package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
  735. package/src/web/styleConversion/enrichedThemingToCSSProperties.ts +34 -0
  736. package/src/web/styleConversion/htmlStyleToCSSVariables.ts +277 -0
  737. package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
  738. package/src/web/styleConversion/mentionIndicatorCssKey.ts +14 -0
  739. package/src/web/styleConversion/toColor.ts +17 -0
  740. package/src/web/useImageErrorFallback.ts +37 -0
  741. package/src/web/useOnChangeHtml.ts +14 -0
  742. package/src/web/useOnChangeState.ts +127 -0
  743. package/src/web/useOnChangeText.ts +15 -0
  744. package/src/web/useOnEditorChange.ts +33 -0
  745. package/src/web/useOnLinkDetected.ts +70 -0
  746. package/src/web/usePressInteractions.tsx +22 -0
  747. package/src/web/useStableRef.ts +12 -0
@@ -0,0 +1,1486 @@
1
+ #import "HtmlParser.h"
2
+ #import "AlignmentEntry.h"
3
+ #import "AlignmentUtils.h"
4
+ #import "EnrichedConfig.h"
5
+ #import "ImageData.h"
6
+ #import "LinkData.h"
7
+ #import "MentionParams.h"
8
+ #import "StringExtension.h"
9
+ #import "StyleHeaders.h"
10
+ #import "StylePair.h"
11
+
12
+ #include "GumboParser.hpp"
13
+
14
+ @implementation HtmlParser
15
+
16
+ + (BOOL)isBlockTag:(NSString *)tagName {
17
+ return [tagName isEqualToString:@"ul"] || [tagName isEqualToString:@"ol"] ||
18
+ [tagName isEqualToString:@"blockquote"] ||
19
+ [tagName isEqualToString:@"codeblock"];
20
+ }
21
+
22
+ /**
23
+ * Prepares HTML for the parser by stripping extraneous whitespace and newlines
24
+ * from structural tags, while preserving them within text content.
25
+ *
26
+ * APPROACH:
27
+ * This function treats the HTML as having two distinct states:
28
+ * 1. Structure Mode (Depth == 0): We are inside or between container tags (like
29
+ * blockquote, ul, codeblock). In this mode whitespace and newlines are
30
+ * considered layout artifacts and are REMOVED to prevent the parser from
31
+ * creating unwanted spaces.
32
+ * 2. Content Mode (Depth > 0): We are inside a text-containing tag (like p,
33
+ * b, li). In this mode, all whitespace is PRESERVED exactly as is, ensuring
34
+ * that sentences and inline formatting remain readable.
35
+ *
36
+ * The function iterates character-by-character, using a depth counter to track
37
+ * nesting levels of the specific tags defined in `textTags`.
38
+ *
39
+ * IMPORTANT:
40
+ * The `textTags` set acts as a whitelist for "Content Mode". If you add support
41
+ * for a new HTML tag that contains visible text (e.g., h4, h5, h6),
42
+ * you MUST add it to the `textTags` set below.
43
+ */
44
+ + (NSString *)stripExtraWhiteSpacesAndNewlines:(NSString *)html {
45
+ NSSet *textTags = [NSSet setWithObjects:@"p", @"h1", @"h2", @"h3", @"h4",
46
+ @"h5", @"h6", @"li", @"b", @"a", @"s",
47
+ @"mention", @"code", @"u", @"i", nil];
48
+
49
+ NSMutableString *output = [NSMutableString stringWithCapacity:html.length];
50
+ NSMutableString *currentTagBuffer = [NSMutableString string];
51
+ NSCharacterSet *whitespaceAndNewlineSet =
52
+ [NSCharacterSet whitespaceAndNewlineCharacterSet];
53
+
54
+ BOOL isReadingTag = NO;
55
+ NSInteger textDepth = 0;
56
+
57
+ for (NSUInteger i = 0; i < html.length; i++) {
58
+ unichar c = [html characterAtIndex:i];
59
+
60
+ if (c == '<') {
61
+ isReadingTag = YES;
62
+ [currentTagBuffer setString:@""];
63
+ [output appendString:@"<"];
64
+ } else if (c == '>') {
65
+ isReadingTag = NO;
66
+ [output appendString:@">"];
67
+
68
+ NSString *fullTag = [currentTagBuffer lowercaseString];
69
+
70
+ NSString *cleanName = [fullTag
71
+ stringByTrimmingCharactersInSet:
72
+ [NSCharacterSet characterSetWithCharactersInString:@"/"]];
73
+ NSArray *parts =
74
+ [cleanName componentsSeparatedByCharactersInSet:
75
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
76
+ NSString *tagName = parts.firstObject;
77
+
78
+ if (![textTags containsObject:tagName]) {
79
+ continue;
80
+ }
81
+
82
+ if ([fullTag hasPrefix:@"/"]) {
83
+ textDepth--;
84
+ if (textDepth < 0)
85
+ textDepth = 0;
86
+ } else {
87
+ // Opening tag (e.g. <h1>) -> Enter Text Mode
88
+ // (Ignore self-closing tags like <img/> if they happen to be in the
89
+ // list)
90
+ if (![fullTag hasSuffix:@"/"]) {
91
+ textDepth++;
92
+ }
93
+ }
94
+ } else {
95
+ if (isReadingTag) {
96
+ [currentTagBuffer appendFormat:@"%C", c];
97
+ [output appendFormat:@"%C", c];
98
+ continue;
99
+ }
100
+
101
+ if (textDepth > 0) {
102
+ [output appendFormat:@"%C", c];
103
+ } else {
104
+ if (![whitespaceAndNewlineSet characterIsMember:c]) {
105
+ [output appendFormat:@"%C", c];
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ return output;
112
+ }
113
+
114
+ + (NSString *)stringByAddingNewlinesToTag:(NSString *)tag
115
+ inString:(NSString *)html
116
+ leading:(BOOL)leading
117
+ trailing:(BOOL)trailing {
118
+ NSString *str = [html copy];
119
+ if (leading) {
120
+ NSString *formattedTag = [NSString stringWithFormat:@">%@", tag];
121
+ NSString *formattedNewTag = [NSString stringWithFormat:@">\n%@", tag];
122
+ str = [str stringByReplacingOccurrencesOfString:formattedTag
123
+ withString:formattedNewTag];
124
+ }
125
+ if (trailing) {
126
+ NSString *formattedTag = [NSString stringWithFormat:@"%@<", tag];
127
+ NSString *formattedNewTag = [NSString stringWithFormat:@"%@\n<", tag];
128
+ str = [str stringByReplacingOccurrencesOfString:formattedTag
129
+ withString:formattedNewTag];
130
+ }
131
+ return str;
132
+ }
133
+
134
+ #pragma mark - External HTML normalization
135
+
136
+ /**
137
+ * Normalizes external HTML (from Google Docs, Word, web pages, etc.) into our
138
+ * canonical tag subset using the Gumbo-based C++ normalizer.
139
+ *
140
+ * Converts: strong → b, em → i, span style="font-weight:bold" → b,
141
+ * strips unknown tags while preserving text
142
+ */
143
+ + (NSString *_Nullable)normalizeExternalHtml:(NSString *_Nonnull)html {
144
+ std::string result =
145
+ GumboParser::normalizeHtml(std::string([html UTF8String]));
146
+ if (result.empty())
147
+ return nil;
148
+ return [NSString stringWithUTF8String:result.c_str()];
149
+ }
150
+
151
+ + (void)finalizeTagEntry:(NSMutableString *)tagName
152
+ ongoingTags:(NSMutableDictionary *)ongoingTags
153
+ initiallyProcessedTags:(NSMutableArray *)processedTags
154
+ plainText:(NSMutableString *)plainText
155
+ precedingImageCount:(NSInteger *)precedingImageCount {
156
+ NSMutableArray *tagEntry = [[NSMutableArray alloc] init];
157
+
158
+ NSArray *tagData = ongoingTags[tagName];
159
+ if (tagData == nil) {
160
+ return;
161
+ }
162
+ NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
163
+ NSInteger openImageCount = [((NSNumber *)tagData[1]) intValue];
164
+ NSInteger currentImageCount = *precedingImageCount;
165
+
166
+ // 'plainText' doesn't contain image placeholders yet, but the final
167
+ // NSTextStorage will, so each image adds one character that ranges here
168
+ // must account for. 'openImageCount' (captured when the tag opened) shifts
169
+ // the start past images finalized BEFORE this tag, while the diff against
170
+ // 'currentImageCount' extends the length to cover images finalized INSIDE
171
+ // it.
172
+ NSRange tagRange = NSMakeRange(tagLocation + openImageCount,
173
+ (plainText.length - tagLocation) +
174
+ (currentImageCount - openImageCount));
175
+
176
+ [tagEntry addObject:[tagName copy]];
177
+ [tagEntry addObject:[NSValue valueWithRange:tagRange]];
178
+ if (tagData.count > 2) {
179
+ [tagEntry addObject:[(NSString *)tagData[2] copy]];
180
+ }
181
+
182
+ [processedTags addObject:tagEntry];
183
+ [ongoingTags removeObjectForKey:tagName];
184
+
185
+ if ([tagName isEqualToString:@"img"]) {
186
+ (*precedingImageCount)++;
187
+ }
188
+ }
189
+
190
+ + (BOOL)isUlCheckboxList:(NSString *)params {
191
+ return ([params containsString:@"data-type=\"checkbox\""] ||
192
+ [params containsString:@"data-type='checkbox'"]);
193
+ }
194
+
195
+ + (NSDictionary *)prepareCheckboxListStyleValue:(NSValue *)rangeValue
196
+ checkboxStates:(NSDictionary *)checkboxStates {
197
+ NSRange range = [rangeValue rangeValue];
198
+ NSMutableDictionary *statesInRange = [[NSMutableDictionary alloc] init];
199
+
200
+ for (NSNumber *key in checkboxStates) {
201
+ NSUInteger pos = [key unsignedIntegerValue];
202
+ if (pos >= range.location && pos < range.location + range.length) {
203
+ [statesInRange setObject:checkboxStates[key] forKey:key];
204
+ }
205
+ }
206
+
207
+ return statesInRange;
208
+ }
209
+
210
+ + (NSString *_Nullable)initiallyProcessHtml:(NSString *_Nonnull)html
211
+ useHtmlNormalizer:(BOOL)useHtmlNormalizer {
212
+ NSString *htmlWithoutSpaces = [self stripExtraWhiteSpacesAndNewlines:html];
213
+ NSString *fixedHtml = nullptr;
214
+
215
+ if (htmlWithoutSpaces.length >= 13) {
216
+ NSString *firstSix =
217
+ [htmlWithoutSpaces substringWithRange:NSMakeRange(0, 6)];
218
+ NSString *lastSeven = [htmlWithoutSpaces
219
+ substringWithRange:NSMakeRange(htmlWithoutSpaces.length - 7, 7)];
220
+
221
+ if ([firstSix isEqualToString:@"<html>"] &&
222
+ [lastSeven isEqualToString:@"</html>"]) {
223
+ // remove html tags, might be with newlines or without them
224
+ fixedHtml = [htmlWithoutSpaces copy];
225
+ // firstly remove newlined html tags if any:
226
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>\n"
227
+ withString:@""];
228
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"\n</html>"
229
+ withString:@""];
230
+ // fallback; remove html tags without their newlines
231
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<html>"
232
+ withString:@""];
233
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"</html>"
234
+ withString:@""];
235
+ } else if (useHtmlNormalizer) {
236
+ // External HTML (from Google Docs, Word, web pages, etc.)
237
+ // Run through the Gumbo-based normalizer to convert arbitrary HTML
238
+ // into our canonical tag subset.
239
+ NSString *normalized = [self normalizeExternalHtml:html];
240
+ if (normalized != nil) {
241
+ fixedHtml = normalized;
242
+ }
243
+ }
244
+
245
+ // Additionally, try getting the content from between body tags if there are
246
+ // some:
247
+
248
+ // Firstly make sure there are no newlines between them.
249
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<body>\n"
250
+ withString:@"<body>"];
251
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"\n</body>"
252
+ withString:@"</body>"];
253
+ // Then, if there actually are body tags, use the content between them.
254
+ NSRange openingBodyRange = [htmlWithoutSpaces rangeOfString:@"<body>"];
255
+ NSRange closingBodyRange = [htmlWithoutSpaces rangeOfString:@"</body>"];
256
+ if (openingBodyRange.length != 0 && closingBodyRange.length != 0) {
257
+ NSInteger newStart = openingBodyRange.location + 6;
258
+ NSInteger newEnd = closingBodyRange.location - 1;
259
+ fixedHtml = [htmlWithoutSpaces
260
+ substringWithRange:NSMakeRange(newStart, newEnd - newStart + 1)];
261
+ }
262
+ }
263
+
264
+ // second processing - try fixing htmls with wrong newlines' setup
265
+ if (fixedHtml != nullptr) {
266
+ // add <br> tag wherever needed
267
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<p></p>"
268
+ withString:@"<br>"];
269
+
270
+ // remove <p> tags inside of <li>
271
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<li><p>"
272
+ withString:@"<li>"];
273
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"</p></li>"
274
+ withString:@"</li>"];
275
+
276
+ // change <br/> to <br>
277
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<br/>"
278
+ withString:@"<br>"];
279
+
280
+ // remove <p> tags around <br>
281
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<p><br>"
282
+ withString:@"<br>"];
283
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<br></p>"
284
+ withString:@"<br>"];
285
+
286
+ // add <br> tags inside empty blockquote and codeblock tags
287
+ fixedHtml = [fixedHtml
288
+ stringByReplacingOccurrencesOfString:@"<blockquote></blockquote>"
289
+ withString:@"<blockquote><br></"
290
+ @"blockquote>"];
291
+ fixedHtml = [fixedHtml
292
+ stringByReplacingOccurrencesOfString:@"<codeblock></codeblock>"
293
+ withString:@"<codeblock><br></codeblock>"];
294
+
295
+ // remove empty ul and ol tags
296
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<ul></ul>"
297
+ withString:@""];
298
+ fixedHtml = [fixedHtml
299
+ stringByReplacingOccurrencesOfString:@"<ul data-type=\"checkbox\"></ul>"
300
+ withString:@""];
301
+ fixedHtml = [fixedHtml stringByReplacingOccurrencesOfString:@"<ol></ol>"
302
+ withString:@""];
303
+
304
+ // tags that have to be in separate lines
305
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<br>"
306
+ inString:fixedHtml
307
+ leading:YES
308
+ trailing:YES];
309
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<ul>"
310
+ inString:fixedHtml
311
+ leading:YES
312
+ trailing:YES];
313
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</ul>"
314
+ inString:fixedHtml
315
+ leading:YES
316
+ trailing:YES];
317
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<ol>"
318
+ inString:fixedHtml
319
+ leading:YES
320
+ trailing:YES];
321
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</ol>"
322
+ inString:fixedHtml
323
+ leading:YES
324
+ trailing:YES];
325
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<blockquote>"
326
+ inString:fixedHtml
327
+ leading:YES
328
+ trailing:YES];
329
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</blockquote>"
330
+ inString:fixedHtml
331
+ leading:YES
332
+ trailing:YES];
333
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<codeblock>"
334
+ inString:fixedHtml
335
+ leading:YES
336
+ trailing:YES];
337
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</codeblock>"
338
+ inString:fixedHtml
339
+ leading:YES
340
+ trailing:YES];
341
+
342
+ // line opening tags
343
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<p>"
344
+ inString:fixedHtml
345
+ leading:YES
346
+ trailing:NO];
347
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<li>"
348
+ inString:fixedHtml
349
+ leading:YES
350
+ trailing:NO];
351
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<li checked>"
352
+ inString:fixedHtml
353
+ leading:YES
354
+ trailing:NO];
355
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h1>"
356
+ inString:fixedHtml
357
+ leading:YES
358
+ trailing:NO];
359
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h2>"
360
+ inString:fixedHtml
361
+ leading:YES
362
+ trailing:NO];
363
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h3>"
364
+ inString:fixedHtml
365
+ leading:YES
366
+ trailing:NO];
367
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h4>"
368
+ inString:fixedHtml
369
+ leading:YES
370
+ trailing:NO];
371
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h5>"
372
+ inString:fixedHtml
373
+ leading:YES
374
+ trailing:NO];
375
+ fixedHtml = [self stringByAddingNewlinesToTag:@"<h6>"
376
+ inString:fixedHtml
377
+ leading:YES
378
+ trailing:NO];
379
+
380
+ // line closing tags
381
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</p>"
382
+ inString:fixedHtml
383
+ leading:NO
384
+ trailing:YES];
385
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</li>"
386
+ inString:fixedHtml
387
+ leading:NO
388
+ trailing:YES];
389
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h1>"
390
+ inString:fixedHtml
391
+ leading:NO
392
+ trailing:YES];
393
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h2>"
394
+ inString:fixedHtml
395
+ leading:NO
396
+ trailing:YES];
397
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h3>"
398
+ inString:fixedHtml
399
+ leading:NO
400
+ trailing:YES];
401
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h4>"
402
+ inString:fixedHtml
403
+ leading:NO
404
+ trailing:YES];
405
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h5>"
406
+ inString:fixedHtml
407
+ leading:NO
408
+ trailing:YES];
409
+ fixedHtml = [self stringByAddingNewlinesToTag:@"</h6>"
410
+ inString:fixedHtml
411
+ leading:NO
412
+ trailing:YES];
413
+ }
414
+
415
+ return fixedHtml;
416
+ }
417
+
418
+ + (NSArray *_Nonnull)getTextAndStylesFromHtml:(NSString *_Nonnull)fixedHtml
419
+ config:
420
+ (EnrichedConfig *_Nullable)config {
421
+ NSMutableString *plainText = [[NSMutableString alloc] initWithString:@""];
422
+ NSMutableDictionary *ongoingTags = [[NSMutableDictionary alloc] init];
423
+ NSMutableArray *initiallyProcessedTags = [[NSMutableArray alloc] init];
424
+ NSMutableDictionary *checkboxStates = [[NSMutableDictionary alloc] init];
425
+ NSMutableArray<AlignmentEntry *> *foundAlignments =
426
+ [[NSMutableArray alloc] init];
427
+ BOOL insideCheckboxList = NO;
428
+ NSInteger precedingImageCount = 0;
429
+ BOOL insideTag = NO;
430
+ BOOL gettingTagName = NO;
431
+ BOOL gettingTagParams = NO;
432
+ BOOL closingTag = NO;
433
+ BOOL lastTagWasBr = NO;
434
+ NSMutableString *currentTagName =
435
+ [[NSMutableString alloc] initWithString:@""];
436
+ NSMutableString *currentTagParams =
437
+ [[NSMutableString alloc] initWithString:@""];
438
+ NSDictionary *htmlEntitiesDict =
439
+ [NSString getEscapedCharactersInfoFrom:fixedHtml];
440
+
441
+ // firstly, extract text and initially processed tags
442
+ for (int i = 0; i < fixedHtml.length; i++) {
443
+ NSString *currentCharacterStr =
444
+ [fixedHtml substringWithRange:NSMakeRange(i, 1)];
445
+ unichar currentCharacterChar = [fixedHtml characterAtIndex:i];
446
+
447
+ if (currentCharacterChar == '<') {
448
+ // opening the tag, mark that we are inside and getting its name
449
+ insideTag = YES;
450
+ gettingTagName = YES;
451
+ } else if (currentCharacterChar == '>') {
452
+ // finishing some tag, no longer marked as inside or getting its
453
+ // name/params
454
+ insideTag = NO;
455
+ gettingTagName = NO;
456
+ gettingTagParams = NO;
457
+
458
+ BOOL isSelfClosing = NO;
459
+
460
+ // Check if params ended with '/' (e.g. <img src="" />)
461
+ if ([currentTagParams hasSuffix:@"/"]) {
462
+ [currentTagParams
463
+ deleteCharactersInRange:NSMakeRange(currentTagParams.length - 1,
464
+ 1)];
465
+ isSelfClosing = YES;
466
+ }
467
+
468
+ if ([currentTagName isEqualToString:@"br"]) {
469
+ lastTagWasBr = YES;
470
+ // do nothing, we don't include these tags in styles
471
+ } else if ([currentTagName isEqualToString:@"li"]) {
472
+ if (!closingTag) {
473
+ // Opening tag <li>
474
+ // Track checkbox state if we're inside a checkbox list
475
+ if (insideCheckboxList) {
476
+ BOOL isChecked = [currentTagParams containsString:@"checked"];
477
+ checkboxStates[@(plainText.length)] = @(isChecked);
478
+ }
479
+ // Record the start location so we can check if it's empty when
480
+ // closing
481
+ ongoingTags[@"li"] = @[ @(plainText.length) ];
482
+ } else {
483
+ // Closing tag </li>
484
+ NSArray *tagData = ongoingTags[@"li"];
485
+ if (tagData != nil) {
486
+ NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
487
+ NSString *innerContent = [plainText substringFromIndex:tagLocation];
488
+
489
+ // If the li is completely empty (or just contains layout newlines),
490
+ // inject ZWS
491
+ if ([innerContent
492
+ stringByTrimmingCharactersInSet:[NSCharacterSet
493
+ newlineCharacterSet]]
494
+ .length == 0) {
495
+ [plainText appendString:@"\u200B"];
496
+ }
497
+ [ongoingTags removeObjectForKey:@"li"];
498
+ }
499
+ }
500
+ } else if (!closingTag) {
501
+ BOOL isPlainParagraph = [currentTagName isEqualToString:@"p"] &&
502
+ currentTagParams.length == 0;
503
+
504
+ if (!isPlainParagraph) {
505
+ // we finish opening tag - get its location, the current
506
+ // precedingImageCount and optionally params and put them under tag
507
+ // name key in ongoingTags. Storing the open-time image count lets
508
+ // finalizeTagEntry: correctly shift the start and extend the length
509
+ // so the range covers any images finalized between open and close.
510
+ NSMutableArray *tagArr = [[NSMutableArray alloc] init];
511
+ [tagArr addObject:[NSNumber numberWithInteger:plainText.length]];
512
+ [tagArr addObject:[NSNumber numberWithInteger:precedingImageCount]];
513
+ if (currentTagParams.length > 0) {
514
+ [tagArr addObject:[currentTagParams copy]];
515
+ }
516
+ ongoingTags[currentTagName] = tagArr;
517
+
518
+ // Check if this is a checkbox list
519
+ if ([currentTagName isEqualToString:@"ul"] &&
520
+ [self isUlCheckboxList:currentTagParams]) {
521
+ insideCheckboxList = YES;
522
+ }
523
+
524
+ // skip one newline if it was added after opening tags that are in
525
+ // separate lines
526
+ if ([self isBlockTag:currentTagName] && i + 1 < fixedHtml.length &&
527
+ [[NSCharacterSet newlineCharacterSet]
528
+ characterIsMember:[fixedHtml characterAtIndex:i + 1]]) {
529
+ i += 1;
530
+ }
531
+
532
+ if ([currentTagName isEqualToString:@"img"]) {
533
+ // Images have no inner text, so we manually break the <br> streak
534
+ // here.
535
+ lastTagWasBr = NO;
536
+ }
537
+
538
+ if (isSelfClosing) {
539
+ [self finalizeTagEntry:currentTagName
540
+ ongoingTags:ongoingTags
541
+ initiallyProcessedTags:initiallyProcessedTags
542
+ plainText:plainText
543
+ precedingImageCount:&precedingImageCount];
544
+ }
545
+ }
546
+ } else {
547
+ // we finish closing tags - pack tag name, tag range and optionally tag
548
+ // params into an entry that goes inside initiallyProcessedTags
549
+
550
+ // Check if we're closing a checkbox list by looking at the params
551
+ if ([currentTagName isEqualToString:@"ul"] &&
552
+ [self isUlCheckboxList:currentTagParams]) {
553
+ insideCheckboxList = NO;
554
+ }
555
+
556
+ BOOL isBlockTag = [self isBlockTag:currentTagName];
557
+
558
+ // ZWS logic for blockquote and codeblock
559
+ BOOL needsZWS = [currentTagName isEqualToString:@"blockquote"] ||
560
+ [currentTagName isEqualToString:@"codeblock"];
561
+ BOOL isEmptyBlock = NO;
562
+ if (needsZWS) {
563
+ NSArray *tagData = ongoingTags[currentTagName];
564
+ if (tagData != nil) {
565
+ NSInteger tagLoc = [tagData[0] intValue];
566
+ NSString *inner = [plainText substringFromIndex:tagLoc];
567
+ if ([inner stringByTrimmingCharactersInSet:[NSCharacterSet
568
+ newlineCharacterSet]]
569
+ .length == 0) {
570
+ isEmptyBlock = YES;
571
+ }
572
+ }
573
+ }
574
+
575
+ // skip one newline if it was added before some closing tags that are
576
+ // in separate lines
577
+ if (isBlockTag && plainText.length > 0 &&
578
+ [[NSCharacterSet newlineCharacterSet]
579
+ characterIsMember:[plainText
580
+ characterAtIndex:plainText.length - 1]]) {
581
+
582
+ // If the last thing processed was a <br>, or the block is totally
583
+ // empty, inject a \u200B before trimming the trailing newline to save
584
+ // the empty line.
585
+ if (lastTagWasBr || isEmptyBlock) {
586
+ [plainText insertString:@"\u200B" atIndex:plainText.length - 1];
587
+ }
588
+ plainText = [[plainText
589
+ substringWithRange:NSMakeRange(0, plainText.length - 1)]
590
+ mutableCopy];
591
+ }
592
+
593
+ [self checkForAlignments:ongoingTags[currentTagName]
594
+ plainText:plainText
595
+ foundAlignments:foundAlignments
596
+ precedingImageCount:precedingImageCount];
597
+ [self finalizeTagEntry:currentTagName
598
+ ongoingTags:ongoingTags
599
+ initiallyProcessedTags:initiallyProcessedTags
600
+ plainText:plainText
601
+ precedingImageCount:&precedingImageCount];
602
+ }
603
+ // post-tag cleanup
604
+ closingTag = NO;
605
+ currentTagName = [[NSMutableString alloc] initWithString:@""];
606
+ currentTagParams = [[NSMutableString alloc] initWithString:@""];
607
+ } else {
608
+ if (!insideTag) {
609
+ // no tags logic - just append the right text
610
+
611
+ // html entity on the index; use unescaped character and forward
612
+ // iterator accordingly
613
+ NSArray *entityInfo = htmlEntitiesDict[@(i)];
614
+ if (entityInfo != nullptr) {
615
+ NSString *escaped = entityInfo[0];
616
+ NSString *unescaped = entityInfo[1];
617
+ [plainText appendString:unescaped];
618
+ // the iterator will forward by 1 itself
619
+ i += escaped.length - 1;
620
+ } else {
621
+ [plainText appendString:currentCharacterStr];
622
+ // Any typed character that isn't a newline breaks the <br> streak
623
+ if (![[NSCharacterSet newlineCharacterSet]
624
+ characterIsMember:currentCharacterChar]) {
625
+ lastTagWasBr = NO;
626
+ }
627
+ }
628
+ } else {
629
+ if (gettingTagName) {
630
+ if (currentCharacterChar == ' ') {
631
+ // no longer getting tag name - switch to params
632
+ gettingTagName = NO;
633
+ gettingTagParams = YES;
634
+ } else if (currentCharacterChar == '/') {
635
+ // mark that the tag is closing
636
+ closingTag = YES;
637
+ } else {
638
+ // append next tag char
639
+ [currentTagName appendString:currentCharacterStr];
640
+ }
641
+ } else if (gettingTagParams) {
642
+ // append next tag params char
643
+ [currentTagParams appendString:currentCharacterStr];
644
+ }
645
+ }
646
+ }
647
+ }
648
+
649
+ // process tags into proper StyleType + StylePair values
650
+ NSMutableArray *processedStyles = [[NSMutableArray alloc] init];
651
+ // Tracks the number of processed images to remove their pre-generated
652
+ // placeholder offsets from tag ranges when reading from plainText
653
+ // (which does not contain those placeholders).
654
+ NSInteger secondPassImageCount = 0;
655
+
656
+ for (NSArray *arr in initiallyProcessedTags) {
657
+ NSString *tagName = (NSString *)arr[0];
658
+ NSValue *tagRangeValue = (NSValue *)arr[1];
659
+ NSMutableString *params = [[NSMutableString alloc] initWithString:@""];
660
+ if (arr.count > 2) {
661
+ [params appendString:(NSString *)arr[2]];
662
+ }
663
+
664
+ NSMutableArray *styleArr = [[NSMutableArray alloc] init];
665
+ StylePair *stylePair = [[StylePair alloc] init];
666
+
667
+ if ([tagName isEqualToString:@"b"]) {
668
+ [styleArr addObject:@([BoldStyle getType])];
669
+ } else if ([tagName isEqualToString:@"i"]) {
670
+ [styleArr addObject:@([ItalicStyle getType])];
671
+ } else if ([tagName isEqualToString:@"img"]) {
672
+ NSRegularExpression *srcRegex =
673
+ [NSRegularExpression regularExpressionWithPattern:@"src=\"([^\"]+)\""
674
+ options:0
675
+ error:nullptr];
676
+ NSTextCheckingResult *match =
677
+ [srcRegex firstMatchInString:params
678
+ options:0
679
+ range:NSMakeRange(0, params.length)];
680
+
681
+ if (match == nullptr) {
682
+ continue;
683
+ }
684
+
685
+ NSRange srcRange = match.range;
686
+ [styleArr addObject:@([ImageStyle getType])];
687
+ // cut only the uri from the src="..." string
688
+ NSString *uri =
689
+ [params substringWithRange:NSMakeRange(srcRange.location + 5,
690
+ srcRange.length - 6)];
691
+ ImageData *imageData = [[ImageData alloc] init];
692
+ imageData.uri = uri;
693
+
694
+ NSRegularExpression *widthRegex = [NSRegularExpression
695
+ regularExpressionWithPattern:@"width=\"([0-9.]+)\""
696
+ options:0
697
+ error:nil];
698
+ NSTextCheckingResult *widthMatch =
699
+ [widthRegex firstMatchInString:params
700
+ options:0
701
+ range:NSMakeRange(0, params.length)];
702
+
703
+ if (widthMatch) {
704
+ NSString *widthString =
705
+ [params substringWithRange:[widthMatch rangeAtIndex:1]];
706
+ imageData.width = [widthString floatValue];
707
+ }
708
+
709
+ NSRegularExpression *heightRegex = [NSRegularExpression
710
+ regularExpressionWithPattern:@"height=\"([0-9.]+)\""
711
+ options:0
712
+ error:nil];
713
+ NSTextCheckingResult *heightMatch =
714
+ [heightRegex firstMatchInString:params
715
+ options:0
716
+ range:NSMakeRange(0, params.length)];
717
+
718
+ if (heightMatch) {
719
+ NSString *heightString =
720
+ [params substringWithRange:[heightMatch rangeAtIndex:1]];
721
+ imageData.height = [heightString floatValue];
722
+ }
723
+
724
+ stylePair.styleValue = imageData;
725
+ secondPassImageCount++;
726
+ } else if ([tagName isEqualToString:@"u"]) {
727
+ [styleArr addObject:@([UnderlineStyle getType])];
728
+ } else if ([tagName isEqualToString:@"s"]) {
729
+ [styleArr addObject:@([StrikethroughStyle getType])];
730
+ } else if ([tagName isEqualToString:@"code"]) {
731
+ [styleArr addObject:@([InlineCodeStyle getType])];
732
+ } else if ([tagName isEqualToString:@"a"]) {
733
+ NSRegularExpression *hrefRegex =
734
+ [NSRegularExpression regularExpressionWithPattern:@"href=\".+\""
735
+ options:0
736
+ error:nullptr];
737
+ NSTextCheckingResult *match =
738
+ [hrefRegex firstMatchInString:params
739
+ options:0
740
+ range:NSMakeRange(0, params.length)];
741
+
742
+ if (match == nullptr) {
743
+ // same as on Android, no href (or empty href) equals no link style
744
+ continue;
745
+ }
746
+
747
+ NSRange hrefRange = match.range;
748
+ [styleArr addObject:@([LinkStyle getType])];
749
+ // cut only the url from the href="..." string
750
+ NSString *url =
751
+ [params substringWithRange:NSMakeRange(hrefRange.location + 6,
752
+ hrefRange.length - 7)];
753
+
754
+ // tagRange location includes one extra offset per preceding image
755
+ // placeholder, which don't exist in plainText. Subtract them to map
756
+ // back to the correct plainText index.
757
+ NSRange adjustedRange = tagRangeValue.rangeValue;
758
+ NSRange plainTextRange = NSMakeRange(
759
+ adjustedRange.location - secondPassImageCount, adjustedRange.length);
760
+ NSString *text = [plainText substringWithRange:plainTextRange];
761
+
762
+ LinkData *linkData = [[LinkData alloc] init];
763
+ linkData.url = url;
764
+ linkData.text = text;
765
+ linkData.isManual = ![text isEqualToString:url] ||
766
+ ![LinkStyle matchesLinkRegexWithConfig:url
767
+ config:config];
768
+
769
+ stylePair.styleValue = linkData;
770
+ } else if ([tagName isEqualToString:@"mention"]) {
771
+ [styleArr addObject:@([MentionStyle getType])];
772
+ // extract html expression into dict using some regex
773
+ NSMutableDictionary *paramsDict = [[NSMutableDictionary alloc] init];
774
+ NSString *pattern = @"(\\w+)=(['\"])(.*?)\\2";
775
+ NSRegularExpression *regex =
776
+ [NSRegularExpression regularExpressionWithPattern:pattern
777
+ options:0
778
+ error:nil];
779
+
780
+ [regex enumerateMatchesInString:params
781
+ options:0
782
+ range:NSMakeRange(0, params.length)
783
+ usingBlock:^(NSTextCheckingResult *_Nullable result,
784
+ NSMatchingFlags flags,
785
+ BOOL *_Nonnull stop) {
786
+ if (result.numberOfRanges == 4) {
787
+ NSString *key = [params
788
+ substringWithRange:[result rangeAtIndex:1]];
789
+ NSString *value = [params
790
+ substringWithRange:[result rangeAtIndex:3]];
791
+ paramsDict[key] = value;
792
+ }
793
+ }];
794
+
795
+ MentionParams *mentionParams = [[MentionParams alloc] init];
796
+ mentionParams.text = paramsDict[@"text"];
797
+ mentionParams.indicator = paramsDict[@"indicator"];
798
+
799
+ [paramsDict removeObjectsForKeys:@[ @"text", @"indicator" ]];
800
+ NSError *error;
801
+ NSData *attrsData = [NSJSONSerialization dataWithJSONObject:paramsDict
802
+ options:0
803
+ error:&error];
804
+ NSString *formattedAttrsString =
805
+ [[NSString alloc] initWithData:attrsData
806
+ encoding:NSUTF8StringEncoding];
807
+ mentionParams.attributes = formattedAttrsString;
808
+
809
+ stylePair.styleValue = mentionParams;
810
+ } else if ([tagName isEqualToString:@"h1"]) {
811
+ [styleArr addObject:@([H1Style getType])];
812
+ } else if ([tagName isEqualToString:@"h2"]) {
813
+ [styleArr addObject:@([H2Style getType])];
814
+ } else if ([tagName isEqualToString:@"h3"]) {
815
+ [styleArr addObject:@([H3Style getType])];
816
+ } else if ([tagName isEqualToString:@"h4"]) {
817
+ [styleArr addObject:@([H4Style getType])];
818
+ } else if ([tagName isEqualToString:@"h5"]) {
819
+ [styleArr addObject:@([H5Style getType])];
820
+ } else if ([tagName isEqualToString:@"h6"]) {
821
+ [styleArr addObject:@([H6Style getType])];
822
+ } else if ([tagName isEqualToString:@"ul"]) {
823
+ if ([self isUlCheckboxList:params]) {
824
+ [styleArr addObject:@([CheckboxListStyle getType])];
825
+ stylePair.styleValue =
826
+ [self prepareCheckboxListStyleValue:tagRangeValue
827
+ checkboxStates:checkboxStates];
828
+ } else {
829
+ [styleArr addObject:@([UnorderedListStyle getType])];
830
+ }
831
+ } else if ([tagName isEqualToString:@"ol"]) {
832
+ [styleArr addObject:@([OrderedListStyle getType])];
833
+ } else if ([tagName isEqualToString:@"blockquote"]) {
834
+ [styleArr addObject:@([BlockQuoteStyle getType])];
835
+ } else if ([tagName isEqualToString:@"codeblock"]) {
836
+ [styleArr addObject:@([CodeBlockStyle getType])];
837
+ } else {
838
+ // some other external tags like span just don't get put into the
839
+ // processed styles
840
+ continue;
841
+ }
842
+
843
+ stylePair.rangeValue = tagRangeValue;
844
+ [styleArr addObject:stylePair];
845
+ [processedStyles addObject:styleArr];
846
+ }
847
+
848
+ return @[ plainText, processedStyles, foundAlignments ];
849
+ }
850
+
851
+ + (NSString *)parseToHtmlFromRange:(NSRange)range
852
+ host:(id<EnrichedViewHost>)host {
853
+ NSInteger offset = range.location;
854
+ NSString *text = [host.textView.textStorage.string substringWithRange:range];
855
+
856
+ if (text.length == 0) {
857
+ return @"<html>\n<p></p>\n</html>";
858
+ }
859
+
860
+ NSMutableString *result = [[NSMutableString alloc] initWithString:@"<html>"];
861
+ NSSet<NSNumber *> *previousActiveStyles = [[NSSet<NSNumber *> alloc] init];
862
+ BOOL newLine = YES;
863
+ BOOL inUnorderedList = NO;
864
+ BOOL inOrderedList = NO;
865
+ BOOL inBlockQuote = NO;
866
+ BOOL inCodeBlock = NO;
867
+ BOOL inCheckboxList = NO;
868
+ unichar lastCharacter = 0;
869
+
870
+ for (int i = 0; i < text.length; i++) {
871
+ NSRange currentRange = NSMakeRange(offset + i, 1);
872
+ NSMutableSet<NSNumber *> *currentActiveStyles =
873
+ [[NSMutableSet<NSNumber *> alloc] init];
874
+ NSMutableDictionary *currentActiveStylesBeginning =
875
+ [[NSMutableDictionary alloc] init];
876
+
877
+ // check each existing style existence
878
+ for (NSNumber *type in host.stylesDict) {
879
+ StyleBase *style = host.stylesDict[type];
880
+ // we do not want to add <></> tags for alignment
881
+ if ([style isKindOfClass:[AlignmentStyle class]]) {
882
+ continue;
883
+ }
884
+ if ([style detect:currentRange]) {
885
+ [currentActiveStyles addObject:type];
886
+
887
+ if (![previousActiveStyles member:type]) {
888
+ currentActiveStylesBeginning[type] = [NSNumber numberWithInt:i];
889
+ }
890
+ } else if ([previousActiveStyles member:type]) {
891
+ [currentActiveStylesBeginning removeObjectForKey:type];
892
+ }
893
+ }
894
+
895
+ NSString *currentCharacterStr =
896
+ [host.textView.textStorage.string substringWithRange:currentRange];
897
+ unichar currentCharacterChar = [host.textView.textStorage.string
898
+ characterAtIndex:currentRange.location];
899
+
900
+ if ([[NSCharacterSet newlineCharacterSet]
901
+ characterIsMember:currentCharacterChar]) {
902
+ if (newLine) {
903
+ // we can either have an empty list item OR need to close the list and
904
+ // put a BR in such a situation the existence of the list must be
905
+ // checked on 0 length range, not on the newline character
906
+ if (inOrderedList) {
907
+ OrderedListStyle *oStyle = host.stylesDict[@(OrderedList)];
908
+ BOOL detected = [oStyle detect:NSMakeRange(currentRange.location, 0)];
909
+ if (detected) {
910
+ [result appendString:@"\n<li></li>"];
911
+ } else {
912
+ [result appendString:@"\n</ol>\n<br>"];
913
+ inOrderedList = NO;
914
+ }
915
+ } else if (inUnorderedList) {
916
+ UnorderedListStyle *uStyle = host.stylesDict[@(UnorderedList)];
917
+ BOOL detected = [uStyle detect:NSMakeRange(currentRange.location, 0)];
918
+ if (detected) {
919
+ [result appendString:@"\n<li></li>"];
920
+ } else {
921
+ [result appendString:@"\n</ul>\n<br>"];
922
+ inUnorderedList = NO;
923
+ }
924
+ } else if (inBlockQuote) {
925
+ BlockQuoteStyle *bqStyle = host.stylesDict[@(BlockQuote)];
926
+ BOOL detected =
927
+ [bqStyle detect:NSMakeRange(currentRange.location, 0)];
928
+ if (detected) {
929
+ [result appendString:@"\n<br>"];
930
+ } else {
931
+ [result appendString:@"\n</blockquote>\n<br>"];
932
+ inBlockQuote = NO;
933
+ }
934
+ } else if (inCodeBlock) {
935
+ CodeBlockStyle *cbStyle = host.stylesDict[@(CodeBlock)];
936
+ BOOL detected =
937
+ [cbStyle detect:NSMakeRange(currentRange.location, 0)];
938
+ if (detected) {
939
+ [result appendString:@"\n<br>"];
940
+ } else {
941
+ [result appendString:@"\n</codeblock>\n<br>"];
942
+ inCodeBlock = NO;
943
+ }
944
+ } else if (inCheckboxList) {
945
+ CheckboxListStyle *cbLStyle = host.stylesDict[@(CheckboxList)];
946
+ BOOL detected =
947
+ [cbLStyle detect:NSMakeRange(currentRange.location, 0)];
948
+ if (detected) {
949
+ BOOL checked = [cbLStyle getCheckboxStateAt:currentRange.location];
950
+ if (checked) {
951
+ [result appendString:@"\n<li checked></li>"];
952
+ } else {
953
+ [result appendString:@"\n<li></li>"];
954
+ }
955
+ } else {
956
+ [result appendString:@"\n</ul>\n<br>"];
957
+ inCheckboxList = NO;
958
+ }
959
+ } else {
960
+ [result appendString:@"\n<br>"];
961
+ }
962
+ } else {
963
+ // newline finishes a paragraph and all style tags need to be closed
964
+ // we use previous styles
965
+ NSArray<NSNumber *> *sortedEndedStyles = [previousActiveStyles
966
+ sortedArrayUsingDescriptors:@[ [NSSortDescriptor
967
+ sortDescriptorWithKey:@"intValue"
968
+ ascending:NO] ]];
969
+
970
+ // append closing tags
971
+ for (NSNumber *style in sortedEndedStyles) {
972
+ if ([style isEqualToNumber:@([ImageStyle getType])]) {
973
+ continue;
974
+ }
975
+ NSString *tagContent = [self tagContentForStyle:style
976
+ openingTag:NO
977
+ location:currentRange.location
978
+ host:host];
979
+ [result
980
+ appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
981
+ }
982
+
983
+ // append closing paragraph tag
984
+ if ([previousActiveStyles
985
+ containsObject:@([UnorderedListStyle getType])] ||
986
+ [previousActiveStyles
987
+ containsObject:@([OrderedListStyle getType])] ||
988
+ [previousActiveStyles containsObject:@([H1Style getType])] ||
989
+ [previousActiveStyles containsObject:@([H2Style getType])] ||
990
+ [previousActiveStyles containsObject:@([H3Style getType])] ||
991
+ [previousActiveStyles containsObject:@([H4Style getType])] ||
992
+ [previousActiveStyles containsObject:@([H5Style getType])] ||
993
+ [previousActiveStyles containsObject:@([H6Style getType])] ||
994
+ [previousActiveStyles
995
+ containsObject:@([BlockQuoteStyle getType])] ||
996
+ [previousActiveStyles containsObject:@([CodeBlockStyle getType])] ||
997
+ [previousActiveStyles
998
+ containsObject:@([CheckboxListStyle getType])]) {
999
+ // do nothing, proper closing paragraph tags have been already
1000
+ // appended
1001
+ } else {
1002
+ [result appendString:@"</p>"];
1003
+ }
1004
+ }
1005
+
1006
+ // clear the previous styles
1007
+ previousActiveStyles = [[NSSet<NSNumber *> alloc] init];
1008
+
1009
+ // next character opens new paragraph
1010
+ newLine = YES;
1011
+ } else {
1012
+ // new line - open the paragraph
1013
+ if (newLine) {
1014
+ newLine = NO;
1015
+
1016
+ // handle ending unordered list
1017
+ if (inUnorderedList &&
1018
+ ![currentActiveStyles
1019
+ containsObject:@([UnorderedListStyle getType])]) {
1020
+ inUnorderedList = NO;
1021
+ [result appendString:@"\n</ul>"];
1022
+ }
1023
+ // handle ending ordered list
1024
+ if (inOrderedList &&
1025
+ ![currentActiveStyles
1026
+ containsObject:@([OrderedListStyle getType])]) {
1027
+ inOrderedList = NO;
1028
+ [result appendString:@"\n</ol>"];
1029
+ }
1030
+ // handle ending blockquotes
1031
+ if (inBlockQuote && ![currentActiveStyles
1032
+ containsObject:@([BlockQuoteStyle getType])]) {
1033
+ inBlockQuote = NO;
1034
+ [result appendString:@"\n</blockquote>"];
1035
+ }
1036
+ // handle ending codeblock
1037
+ if (inCodeBlock &&
1038
+ ![currentActiveStyles containsObject:@([CodeBlockStyle getType])]) {
1039
+ inCodeBlock = NO;
1040
+ [result appendString:@"\n</codeblock>"];
1041
+ }
1042
+ // handle ending checkbox list
1043
+ if (inCheckboxList &&
1044
+ ![currentActiveStyles
1045
+ containsObject:@([CheckboxListStyle getType])]) {
1046
+ inCheckboxList = NO;
1047
+ [result appendString:@"\n</ul>"];
1048
+ }
1049
+
1050
+ NSString *cssStyleString =
1051
+ [self prepareCssStyleString:currentRange.location
1052
+ isOpeningTag:YES
1053
+ host:host];
1054
+
1055
+ // handle starting unordered list
1056
+ if (!inUnorderedList &&
1057
+ [currentActiveStyles
1058
+ containsObject:@([UnorderedListStyle getType])]) {
1059
+ inUnorderedList = YES;
1060
+ [result appendString:[NSString stringWithFormat:@"\n<ul%@>",
1061
+ cssStyleString]];
1062
+ }
1063
+ // handle starting ordered list
1064
+ if (!inOrderedList &&
1065
+ [currentActiveStyles
1066
+ containsObject:@([OrderedListStyle getType])]) {
1067
+ inOrderedList = YES;
1068
+ [result appendString:[NSString stringWithFormat:@"\n<ol%@>",
1069
+ cssStyleString]];
1070
+ }
1071
+ // handle starting blockquotes
1072
+ if (!inBlockQuote &&
1073
+ [currentActiveStyles containsObject:@([BlockQuoteStyle getType])]) {
1074
+ inBlockQuote = YES;
1075
+ [result appendString:@"\n<blockquote>"];
1076
+ }
1077
+ // handle starting codeblock
1078
+ if (!inCodeBlock &&
1079
+ [currentActiveStyles containsObject:@([CodeBlockStyle getType])]) {
1080
+ inCodeBlock = YES;
1081
+ [result appendString:@"\n<codeblock>"];
1082
+ }
1083
+ // handle starting checkbox list
1084
+ if (!inCheckboxList &&
1085
+ [currentActiveStyles
1086
+ containsObject:@([CheckboxListStyle getType])]) {
1087
+ inCheckboxList = YES;
1088
+ [result appendString:[NSString stringWithFormat:
1089
+ @"\n<ul data-type=\"checkbox\"%@>",
1090
+ cssStyleString]];
1091
+ }
1092
+
1093
+ // don't add the <p> tag if some paragraph styles are present
1094
+ if ([currentActiveStyles
1095
+ containsObject:@([UnorderedListStyle getType])] ||
1096
+ [currentActiveStyles
1097
+ containsObject:@([OrderedListStyle getType])] ||
1098
+ [currentActiveStyles containsObject:@([H1Style getType])] ||
1099
+ [currentActiveStyles containsObject:@([H2Style getType])] ||
1100
+ [currentActiveStyles containsObject:@([H3Style getType])] ||
1101
+ [currentActiveStyles containsObject:@([H4Style getType])] ||
1102
+ [currentActiveStyles containsObject:@([H5Style getType])] ||
1103
+ [currentActiveStyles containsObject:@([H6Style getType])] ||
1104
+ [currentActiveStyles containsObject:@([BlockQuoteStyle getType])] ||
1105
+ [currentActiveStyles containsObject:@([CodeBlockStyle getType])] ||
1106
+ [currentActiveStyles
1107
+ containsObject:@([CheckboxListStyle getType])]) {
1108
+ [result appendString:@"\n"];
1109
+ } else {
1110
+ [result appendString:[NSString stringWithFormat:@"\n<p%@>",
1111
+ cssStyleString]];
1112
+ }
1113
+ }
1114
+
1115
+ // get styles that have ended
1116
+ NSMutableSet<NSNumber *> *endedStyles =
1117
+ [previousActiveStyles mutableCopy];
1118
+ [endedStyles minusSet:currentActiveStyles];
1119
+
1120
+ // also finish styles that should be ended becasue they are nested in a
1121
+ // style that ended
1122
+ NSMutableSet *fixedEndedStyles = [endedStyles mutableCopy];
1123
+ NSMutableSet *stylesToBeReAdded = [[NSMutableSet alloc] init];
1124
+
1125
+ for (NSNumber *style in endedStyles) {
1126
+ NSInteger styleBeginning =
1127
+ [currentActiveStylesBeginning[style] integerValue];
1128
+
1129
+ for (NSNumber *activeStyle in currentActiveStyles) {
1130
+ NSInteger activeStyleBeginning =
1131
+ [currentActiveStylesBeginning[activeStyle] integerValue];
1132
+
1133
+ // we end the styles that began after the currently ended style but
1134
+ // not at the "i" (cause the old style ended at exactly "i-1" also the
1135
+ // ones that began in the exact same place but are "inner" in relation
1136
+ // to them due to StyleTypeEnum integer values
1137
+
1138
+ if ((activeStyleBeginning > styleBeginning &&
1139
+ activeStyleBeginning < i) ||
1140
+ (activeStyleBeginning == styleBeginning &&
1141
+ activeStyleBeginning<
1142
+ i && [activeStyle integerValue]>[style integerValue])) {
1143
+ [fixedEndedStyles addObject:activeStyle];
1144
+ [stylesToBeReAdded addObject:activeStyle];
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ // if a style begins but there is a style inner to it that is (and was
1150
+ // previously) active, it also should be closed and readded
1151
+
1152
+ // newly added styles
1153
+ NSMutableSet *newStyles = [currentActiveStyles mutableCopy];
1154
+ [newStyles minusSet:previousActiveStyles];
1155
+ // styles that were and still are active
1156
+ NSMutableSet *stillActiveStyles = [previousActiveStyles mutableCopy];
1157
+ [stillActiveStyles intersectSet:currentActiveStyles];
1158
+
1159
+ for (NSNumber *style in newStyles) {
1160
+ for (NSNumber *ongoingStyle in stillActiveStyles) {
1161
+ if ([ongoingStyle integerValue] > [style integerValue]) {
1162
+ // the prev style is inner; needs to be closed and re-added later
1163
+ [fixedEndedStyles addObject:ongoingStyle];
1164
+ [stylesToBeReAdded addObject:ongoingStyle];
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ // they are sorted in a descending order
1170
+ NSArray<NSNumber *> *sortedEndedStyles = [fixedEndedStyles
1171
+ sortedArrayUsingDescriptors:@[ [NSSortDescriptor
1172
+ sortDescriptorWithKey:@"intValue"
1173
+ ascending:NO] ]];
1174
+
1175
+ // append closing tags
1176
+ for (NSNumber *style in sortedEndedStyles) {
1177
+ if ([style isEqualToNumber:@([ImageStyle getType])]) {
1178
+ continue;
1179
+ }
1180
+ NSString *tagContent = [self tagContentForStyle:style
1181
+ openingTag:NO
1182
+ location:currentRange.location
1183
+ host:host];
1184
+ [result appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
1185
+ }
1186
+
1187
+ // all styles that have begun: new styles + the ones that need to be
1188
+ // re-added they are sorted in a ascending manner to properly keep tags'
1189
+ // FILO order
1190
+ [newStyles unionSet:stylesToBeReAdded];
1191
+ NSArray<NSNumber *> *sortedNewStyles = [newStyles
1192
+ sortedArrayUsingDescriptors:@[ [NSSortDescriptor
1193
+ sortDescriptorWithKey:@"intValue"
1194
+ ascending:YES] ]];
1195
+
1196
+ // append opening tags
1197
+ for (NSNumber *style in sortedNewStyles) {
1198
+ NSString *tagContent = [self tagContentForStyle:style
1199
+ openingTag:YES
1200
+ location:currentRange.location
1201
+ host:host];
1202
+ if ([style isEqualToNumber:@([ImageStyle getType])]) {
1203
+ [result
1204
+ appendString:[NSString stringWithFormat:@"<%@/>", tagContent]];
1205
+ [currentActiveStyles removeObject:@([ImageStyle getType])];
1206
+ } else {
1207
+ [result appendString:[NSString stringWithFormat:@"<%@>", tagContent]];
1208
+ }
1209
+ }
1210
+
1211
+ // append the letter and escape it if needed
1212
+ [result appendString:[NSString stringByEscapingHtml:currentCharacterStr]];
1213
+
1214
+ // save current styles for next character's checks
1215
+ previousActiveStyles = currentActiveStyles;
1216
+ }
1217
+
1218
+ // set last character
1219
+ lastCharacter = currentCharacterChar;
1220
+ }
1221
+
1222
+ if (![[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) {
1223
+ // not-newline character was last - finish the paragraph
1224
+ // close all pending tags
1225
+ NSArray<NSNumber *> *sortedEndedStyles = [previousActiveStyles
1226
+ sortedArrayUsingDescriptors:@[ [NSSortDescriptor
1227
+ sortDescriptorWithKey:@"intValue"
1228
+ ascending:NO] ]];
1229
+
1230
+ // append closing tags
1231
+ for (NSNumber *style in sortedEndedStyles) {
1232
+ if ([style isEqualToNumber:@([ImageStyle getType])]) {
1233
+ continue;
1234
+ }
1235
+ NSString *tagContent =
1236
+ [self tagContentForStyle:style
1237
+ openingTag:NO
1238
+ location:host.textView.textStorage.string.length - 1
1239
+ host:host];
1240
+ [result appendString:[NSString stringWithFormat:@"</%@>", tagContent]];
1241
+ }
1242
+
1243
+ // finish the paragraph
1244
+ // handle ending of some paragraph styles
1245
+ if ([previousActiveStyles containsObject:@([UnorderedListStyle getType])]) {
1246
+ [result appendString:@"\n</ul>"];
1247
+ } else if ([previousActiveStyles
1248
+ containsObject:@([OrderedListStyle getType])]) {
1249
+ [result appendString:@"\n</ol>"];
1250
+ } else if ([previousActiveStyles
1251
+ containsObject:@([BlockQuoteStyle getType])]) {
1252
+ [result appendString:@"\n</blockquote>"];
1253
+ } else if ([previousActiveStyles
1254
+ containsObject:@([CodeBlockStyle getType])]) {
1255
+ [result appendString:@"\n</codeblock>"];
1256
+ } else if ([previousActiveStyles
1257
+ containsObject:@([CheckboxListStyle getType])]) {
1258
+ [result appendString:@"\n</ul>"];
1259
+ } else if ([previousActiveStyles containsObject:@([H1Style getType])] ||
1260
+ [previousActiveStyles containsObject:@([H2Style getType])] ||
1261
+ [previousActiveStyles containsObject:@([H3Style getType])] ||
1262
+ [previousActiveStyles containsObject:@([H4Style getType])] ||
1263
+ [previousActiveStyles containsObject:@([H5Style getType])] ||
1264
+ [previousActiveStyles containsObject:@([H6Style getType])]) {
1265
+ // do nothing, heading closing tag has already been appended
1266
+ } else {
1267
+ [result appendString:@"</p>"];
1268
+ }
1269
+ } else {
1270
+ // newline character was last - some paragraph styles need to be closed
1271
+ if (inUnorderedList) {
1272
+ inUnorderedList = NO;
1273
+ [result appendString:@"\n</ul>"];
1274
+ }
1275
+ if (inOrderedList) {
1276
+ inOrderedList = NO;
1277
+ [result appendString:@"\n</ol>"];
1278
+ }
1279
+ if (inBlockQuote) {
1280
+ inBlockQuote = NO;
1281
+ [result appendString:@"\n</blockquote>"];
1282
+ }
1283
+ if (inCodeBlock) {
1284
+ inCodeBlock = NO;
1285
+ [result appendString:@"\n</codeblock>"];
1286
+ }
1287
+ if (inCheckboxList) {
1288
+ inCheckboxList = NO;
1289
+ [result appendString:@"\n</ul>"];
1290
+ }
1291
+ }
1292
+
1293
+ [result appendString:@"\n</html>"];
1294
+
1295
+ // remove Object Replacement Characters in the very end
1296
+ [result replaceOccurrencesOfString:@"\uFFFC"
1297
+ withString:@""
1298
+ options:0
1299
+ range:NSMakeRange(0, result.length)];
1300
+
1301
+ // remove zero width spaces in the very end
1302
+ [result replaceOccurrencesOfString:@"\u200B"
1303
+ withString:@""
1304
+ options:0
1305
+ range:NSMakeRange(0, result.length)];
1306
+
1307
+ // replace empty <p></p> into <br> in the very end
1308
+ [result replaceOccurrencesOfString:@"<p></p>"
1309
+ withString:@"<br>"
1310
+ options:0
1311
+ range:NSMakeRange(0, result.length)];
1312
+
1313
+ return result;
1314
+ }
1315
+
1316
+ + (NSString *)tagContentForStyle:(NSNumber *)style
1317
+ openingTag:(BOOL)openingTag
1318
+ location:(NSInteger)location
1319
+ host:(id<EnrichedViewHost>)host {
1320
+ NSString *cssStyleString = [self prepareCssStyleString:location
1321
+ isOpeningTag:openingTag
1322
+ host:host];
1323
+ if ([style isEqualToNumber:@([BoldStyle getType])]) {
1324
+ return @"b";
1325
+ } else if ([style isEqualToNumber:@([ItalicStyle getType])]) {
1326
+ return @"i";
1327
+ } else if ([style isEqualToNumber:@([ImageStyle getType])]) {
1328
+ if (openingTag) {
1329
+ ImageStyle *imageStyle =
1330
+ (ImageStyle *)host.stylesDict[@([ImageStyle getType])];
1331
+ if (imageStyle != nullptr) {
1332
+ ImageData *data = [imageStyle getImageDataAt:location];
1333
+ if (data != nullptr && data.uri != nullptr) {
1334
+ return [NSString
1335
+ stringWithFormat:@"img src=\"%@\" width=\"%d\" height=\"%d\"",
1336
+ data.uri, (int)floor(data.width),
1337
+ (int)floor(data.height)];
1338
+ }
1339
+ }
1340
+ return @"img";
1341
+ } else {
1342
+ return @"";
1343
+ }
1344
+ } else if ([style isEqualToNumber:@([UnderlineStyle getType])]) {
1345
+ return @"u";
1346
+ } else if ([style isEqualToNumber:@([StrikethroughStyle getType])]) {
1347
+ return @"s";
1348
+ } else if ([style isEqualToNumber:@([InlineCodeStyle getType])]) {
1349
+ return @"code";
1350
+ } else if ([style isEqualToNumber:@([LinkStyle getType])]) {
1351
+ if (openingTag) {
1352
+ LinkStyle *linkStyle =
1353
+ (LinkStyle *)host.stylesDict[@([LinkStyle getType])];
1354
+ if (linkStyle != nullptr) {
1355
+ LinkData *data = [linkStyle getLinkDataAt:location];
1356
+ if (data != nullptr && data.url != nullptr) {
1357
+ return [NSString stringWithFormat:@"a href=\"%@\"", data.url];
1358
+ }
1359
+ }
1360
+ return @"a";
1361
+ } else {
1362
+ return @"a";
1363
+ }
1364
+ } else if ([style isEqualToNumber:@([MentionStyle getType])]) {
1365
+ if (openingTag) {
1366
+ MentionStyle *mentionStyle =
1367
+ (MentionStyle *)host.stylesDict[@([MentionStyle getType])];
1368
+ if (mentionStyle != nullptr) {
1369
+ MentionParams *params = [mentionStyle getMentionParamsAt:location];
1370
+ // attributes can theoretically be nullptr
1371
+ if (params != nullptr && params.indicator != nullptr &&
1372
+ params.text != nullptr) {
1373
+ NSMutableString *attrsStr =
1374
+ [[NSMutableString alloc] initWithString:@""];
1375
+ if (params.attributes != nullptr) {
1376
+ // turn attributes to Data and then into dict
1377
+ NSData *attrsData =
1378
+ [params.attributes dataUsingEncoding:NSUTF8StringEncoding];
1379
+ NSError *jsonError;
1380
+ NSDictionary *json =
1381
+ [NSJSONSerialization JSONObjectWithData:attrsData
1382
+ options:0
1383
+ error:&jsonError];
1384
+ // format dict keys and values into string
1385
+ [json enumerateKeysAndObjectsUsingBlock:^(
1386
+ id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
1387
+ [attrsStr
1388
+ appendString:[NSString stringWithFormat:@" %@=\"%@\"",
1389
+ (NSString *)key,
1390
+ (NSString *)obj]];
1391
+ }];
1392
+ }
1393
+ return [NSString
1394
+ stringWithFormat:@"mention text=\"%@\" indicator=\"%@\"%@",
1395
+ params.text, params.indicator, attrsStr];
1396
+ }
1397
+ }
1398
+ return @"mention";
1399
+ } else {
1400
+ return @"mention";
1401
+ }
1402
+ } else if ([style isEqualToNumber:@([H1Style getType])]) {
1403
+ return [NSString stringWithFormat:@"h1%@", cssStyleString];
1404
+ } else if ([style isEqualToNumber:@([H2Style getType])]) {
1405
+ return [NSString stringWithFormat:@"h2%@", cssStyleString];
1406
+ } else if ([style isEqualToNumber:@([H3Style getType])]) {
1407
+ return [NSString stringWithFormat:@"h3%@", cssStyleString];
1408
+ } else if ([style isEqualToNumber:@([H4Style getType])]) {
1409
+ return [NSString stringWithFormat:@"h4%@", cssStyleString];
1410
+ } else if ([style isEqualToNumber:@([H5Style getType])]) {
1411
+ return [NSString stringWithFormat:@"h5%@", cssStyleString];
1412
+ } else if ([style isEqualToNumber:@([H6Style getType])]) {
1413
+ return [NSString stringWithFormat:@"h6%@", cssStyleString];
1414
+ } else if ([style isEqualToNumber:@([UnorderedListStyle getType])] ||
1415
+ [style isEqualToNumber:@([OrderedListStyle getType])]) {
1416
+ return @"li";
1417
+ } else if ([style isEqualToNumber:@([CheckboxListStyle getType])]) {
1418
+ if (openingTag) {
1419
+ CheckboxListStyle *checkboxListStyleClass =
1420
+ (CheckboxListStyle *)host.stylesDict[@([CheckboxListStyle getType])];
1421
+ BOOL checked = [checkboxListStyleClass getCheckboxStateAt:location];
1422
+
1423
+ if (checked) {
1424
+ return @"li checked";
1425
+ }
1426
+ return @"li";
1427
+ } else {
1428
+ return @"li";
1429
+ }
1430
+ } else if ([style isEqualToNumber:@([BlockQuoteStyle getType])] ||
1431
+ [style isEqualToNumber:@([CodeBlockStyle getType])]) {
1432
+ // blockquotes and codeblock use <p> tags the same way lists use <li>
1433
+ return [NSString stringWithFormat:@"p%@", cssStyleString];
1434
+ }
1435
+ return @"";
1436
+ }
1437
+
1438
+ + (NSString *)prepareCssStyleString:(NSInteger)location
1439
+ isOpeningTag:(BOOL)isOpeningTag
1440
+ host:(id<EnrichedViewHost>)host {
1441
+ if (!isOpeningTag) {
1442
+ return @"";
1443
+ }
1444
+
1445
+ NSParagraphStyle *pStyle =
1446
+ [host.textView.textStorage attribute:NSParagraphStyleAttributeName
1447
+ atIndex:location
1448
+ effectiveRange:nil];
1449
+ NSString *alignStr = [AlignmentUtils cssValueForAlignment:pStyle.alignment];
1450
+
1451
+ if (alignStr) {
1452
+ return [NSString stringWithFormat:@" style=\"text-align: %@\"", alignStr];
1453
+ }
1454
+
1455
+ return @"";
1456
+ }
1457
+
1458
+ + (void)checkForAlignments:(NSArray *)tagData
1459
+ plainText:(NSString *)plainText
1460
+ foundAlignments:(NSMutableArray<AlignmentEntry *> *)foundAlignments
1461
+ precedingImageCount:(NSInteger)precedingImageCount {
1462
+ if (tagData == nil) {
1463
+ return;
1464
+ }
1465
+
1466
+ // We look at the params stored in ongoingTags
1467
+ NSString *storedParams = (tagData.count > 2) ? tagData[2] : nil;
1468
+ NSTextAlignment align =
1469
+ [AlignmentUtils alignmentFromStyleParams:storedParams];
1470
+
1471
+ if (align != NSTextAlignmentNatural) {
1472
+ NSInteger startLoc = [tagData[0] integerValue];
1473
+ // Calculate range relative to plainText
1474
+ NSInteger actualStart = startLoc + precedingImageCount;
1475
+ NSInteger length = plainText.length - startLoc;
1476
+
1477
+ if (length > 0) {
1478
+ AlignmentEntry *entry = [[AlignmentEntry alloc] init];
1479
+ entry.alignment = align;
1480
+ entry.range = NSMakeRange(actualStart, length);
1481
+ [foundAlignments addObject:entry];
1482
+ }
1483
+ }
1484
+ }
1485
+
1486
+ @end